From 8ee354449f30544c316e80510192fdbaf7580cae Mon Sep 17 00:00:00 2001 From: euzu Date: Sun, 16 Mar 2025 17:08:00 +0100 Subject: [PATCH] fixed some hls streaming issues, added 2 more custom video stream reponse resources --- CHANGELOG.md | 6 + bin/build_github_docker.sh | 4 +- bin/build_github_docker_aarch64.sh | 6 +- bin/build_github_docker_beta.sh | 4 +- bin/build_resources.sh | 28 +-- bin/release.sh | 2 +- config/config.yml | 5 +- docker/Dockerfile | 15 +- docker/Dockerfile-manual | 3 +- ...eeze_frame.jpg => channel_unavailable.jpg} | Bin resources/user_connections_exhausted.jpg | Bin 0 -> 232071 bytes src/api/api_utils.rs | 26 ++- src/api/endpoints/hls_api.rs | 28 +-- src/api/endpoints/m3u_api.rs | 13 +- src/api/endpoints/user_api.rs | 6 +- src/api/endpoints/xmltv_api.rs | 2 +- src/api/endpoints/xtream_api.rs | 28 ++- src/api/model/active_provider_manager.rs | 4 +- ...frame_stream.rs => custom_video_stream.rs} | 6 +- src/api/model/streams/mod.rs | 2 +- src/api/model/streams/provider_stream.rs | 66 +++++-- .../model/streams/provider_stream_factory.rs | 82 ++++---- src/api/model/streams/timed_client_stream.rs | 6 +- src/api/scheduler.rs | 1 - src/foundation/filter.rs | 12 +- src/main.rs | 6 +- src/model/api_proxy.rs | 38 +++- src/model/config.rs | 180 +++++++++++------- src/processing/parser/xmltv.rs | 4 +- src/repository/playlist_repository.rs | 5 +- src/utils/network/request.rs | 64 +++++-- 31 files changed, 399 insertions(+), 253 deletions(-) rename resources/{freeze_frame.jpg => channel_unavailable.jpg} (100%) create mode 100644 resources/user_connections_exhausted.jpg rename src/api/model/streams/{freeze_frame_stream.rs => custom_video_stream.rs} (93%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84d945ee2..0f041a331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog # 2.2.3 (2023-04-xx) - hls reverse proxy implemented +- !BREAKING CHANGE! `channel_unavailable_file` is now under `custom_stream_response`, and new file `user_connections_exhausted` added. +```yaml +custom_stream_response: + channel_unavailable: /home/m3u-filter/channel_unavailable.ts + user_connections_exhausted: /home/m3u-filter/user_connections_exhausted.ts +``` - input alias definition for same provider with same content but different credentials ```yaml - sources: diff --git a/bin/build_github_docker.sh b/bin/build_github_docker.sh index 98519de4c..aad1f2b28 100755 --- a/bin/build_github_docker.sh +++ b/bin/build_github_docker.sh @@ -37,7 +37,7 @@ BIN_FILE=${WORKING_DIR}/target/${TARGET}/release/m3u-filter cp "${WORKING_DIR}/target/${TARGET}/release/m3u-filter" "${DOCKER_DIR}/" rm -rf "${DOCKER_DIR}/web" cp -r "${FRONTEND_DIR}/build" "${DOCKER_DIR}/web" -cp "${RESOURCES_DIR}/freeze_frame.ts" "${DOCKER_DIR}/" +cp -r "${RESOURCES_DIR}"/*.ts "${DOCKER_DIR}/" # Get the version from the binary VERSION=$("$BIN_FILE" -V | sed 's/m3u-filter *//') @@ -74,6 +74,6 @@ docker push ghcr.io/euzu/${ALPINE_IMAGE_NAME}:latest echo "Cleaning up build artifacts..." rm -rf "${DOCKER_DIR}/web" rm -f "${DOCKER_DIR}/m3u-filter" -rm -f "${DOCKER_DIR}/freeze_frame.ts" +rm -f "${DOCKER_DIR}"/*.ts echo "Docker images for version ${VERSION} have been successfully built, tagged, and pushed." diff --git a/bin/build_github_docker_aarch64.sh b/bin/build_github_docker_aarch64.sh index 2c43b79b2..d63e2e5ca 100755 --- a/bin/build_github_docker_aarch64.sh +++ b/bin/build_github_docker_aarch64.sh @@ -70,7 +70,7 @@ fi cp "${WORKING_DIR}/target/${TARGET}/release/m3u-filter" "${DOCKER_DIR}/" rm -rf "${DOCKER_DIR}/web" cp -r "${WORKING_DIR}/frontend/build" "${DOCKER_DIR}/web" -cp -r "${RESOURCES_DIR}/freeze_frame.ts" "${DOCKER_DIR}/" +cp -r "${RESOURCES_DIR}"/*.ts "${DOCKER_DIR}/" cd "${DOCKER_DIR}" echo "Building Docker images for version ${VERSION}" @@ -95,6 +95,6 @@ docker push ghcr.io/euzu/${SCRATCH_IMAGE_NAME}:latest echo "Cleaning up build artifacts..." rm -rf "${DOCKER_DIR}/web" rm -f "${DOCKER_DIR}/m3u-filter" -rm -f "${DOCKER_DIR}/freeze_frame.ts" +rm -f "${DOCKER_DIR}"/*.ts -echo "Docker images ghcr.io/euzu/${SCRATCH_IMAGE_NAME}${VERSION} have been successfully built, tagged, and pushed." +echo "Docker images ghcr.io/euzu/${SCRATCH_IMAGE_NAME}:${VERSION} have been successfully built, tagged, and pushed." diff --git a/bin/build_github_docker_beta.sh b/bin/build_github_docker_beta.sh index c23252107..22452e3bd 100755 --- a/bin/build_github_docker_beta.sh +++ b/bin/build_github_docker_beta.sh @@ -47,7 +47,7 @@ fi cp "${WORKING_DIR}/target/${TARGET}/release/m3u-filter" "${DOCKER_DIR}/" rm -rf "${DOCKER_DIR}/web" cp -r "${WORKING_DIR}/frontend/build" "${DOCKER_DIR}/web" -cp -r "${RESOURCES_DIR}/freeze_frame.ts" "${DOCKER_DIR}/" +cp -r "${RESOURCES_DIR}"/*.ts "${DOCKER_DIR}/" cd "${DOCKER_DIR}" echo "Building Docker images for version ${VERSION}" @@ -68,6 +68,6 @@ docker push ghcr.io/euzu/${SCRATCH_IMAGE_NAME}:latest echo "Cleaning up build artifacts..." rm -rf "${DOCKER_DIR}/web" rm -f "${DOCKER_DIR}/m3u-filter" -rm -f "${DOCKER_DIR}/freeze_frame.ts" +rm -f "${DOCKER_DIR}"/*.ts echo "Docker images ghcr.io/euzu/${SCRATCH_IMAGE_NAME}${VERSION} have been successfully built, tagged, and pushed." diff --git a/bin/build_resources.sh b/bin/build_resources.sh index 8011a2391..889100321 100755 --- a/bin/build_resources.sh +++ b/bin/build_resources.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash - # Function to print usage instructions print_usage() { echo "Usage: $(basename "$0") [-f] [-h]" @@ -22,17 +21,20 @@ while getopts "fh" opt; do esac done -if [ "$flag_force" = false ]; then - if [ -e ./resources/freeze_frame.ts ]; then - echo "Resource exists, skipping creation" - exit; +declare -a resources=("channel_unavailable" "user_connections_exhausted") + +for resource in "${resources[@]}"; do + if [ "$flag_force" = false ]; then + if [ -e "./resources/${resource}.ts" ]; then + echo "Resource ${resource} exists, skipping creation" + continue + fi fi -fi - -if which ffmpeg > /dev/null 2>&1; then - ffmpeg -loop 1 -i ./resources/freeze_frame.jpg -t 10 -r 1 -an -vf "scale=1920:1080" -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p ./resources/freeze_frame.ts -else - echo "ffmpeg not found"; - exit; -fi + if which ffmpeg > /dev/null 2>&1; then + ffmpeg -loop 1 -i "./resources/${resource}.jpg" -t 10 -r 1 -an -vf "scale=1920:1080" -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p "./resources/${resource}.ts" + else + echo "ffmpeg not found" + exit + fi +done diff --git a/bin/release.sh b/bin/release.sh index aeb423003..7fc908637 100755 --- a/bin/release.sh +++ b/bin/release.sh @@ -91,7 +91,7 @@ for PLATFORM in "${!TARGETS[@]}"; do cp "$BIN" "$DIR" cp ../config/*.yml "$DIR" cp -rf "${FRONTEND_DIR}/build" "$DIR"/web - cp "${RESOURCES_DIR}/freeze_frame.ts" "$DIR" + cp -rf "${RESOURCES_DIR}"/*.ts "$DIR" # Create archive for the platform if [[ $PLATFORM == "WINDOWS" ]]; then diff --git a/config/config.yml b/config/config.yml index 1a9a2dff0..bb4c59638 100644 --- a/config/config.yml +++ b/config/config.yml @@ -3,7 +3,10 @@ threads: 0 working_dir: /home/m3u-filter/data backup_dir: /home/m3u-filter/.backup update_on_boot: false -channel_unavailable_file: /home/m3u-filter/freeze_frame.ts +custom_stream_response: + channel_unavailable: /home/m3u-filter/channel_unavailable.ts + user_connections_exhausted: /home/m3u-filter/user_connections_exhausted.ts + # sec min hour day of month month day of week year schedules: - schedule: "0 0 8,12,16,20,22,1 * * * *" diff --git a/docker/Dockerfile b/docker/Dockerfile index e03291493..0af649a0c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,10 +29,15 @@ RUN yarn install --prefer-offline --frozen-lockfile --non-interactive --producti FROM jrottenberg/ffmpeg:latest as resource-build WORKDIR /src COPY resources ./resources -RUN ffmpeg -loop 1 -i ./resources/freeze_frame.jpg -t 10 -r 1 -an \ +RUN ffmpeg -loop 1 -i ./resources/channel_unavailable.jpg -t 10 -r 1 -an \ -vf "scale=1920:1080" \ -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p \ - ./resources/freeze_frame.ts + ./resources/channel_unavailable.ts + +RUN ffmpeg -loop 1 -i ./resources/user_connections_exhausted.jpg -t 10 -r 1 -an \ + -vf "scale=1920:1080" \ + -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p \ + ./resources/user_connections_exhausted.ts \ # Final container (Scratch) FROM scratch as scratch-final @@ -41,7 +46,8 @@ COPY --from=rust-build /usr/share/zoneinfo /usr/share/zoneinfo COPY --from=rust-build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=rust-build /src/target/x86_64-unknown-linux-musl/release/m3u-filter /m3u-filter COPY --from=node-build /app/build /web -COPY --from=resource-build /src/resources/freeze_frame.ts /freeze_frame.ts +COPY --from=resource-build /src/resources/channel_unavailable.ts /channel_unavailable.ts +COPY --from=resource-build /src/resources/user_connections_exhausted.ts /user_connections_exhausted.ts ENTRYPOINT ["/m3u-filter"] CMD ["-s", "-p", "/config"] @@ -51,6 +57,7 @@ RUN apk add --no-cache bash curl ca-certificates tini WORKDIR /app COPY --from=rust-build /src/target/x86_64-unknown-linux-musl/release/m3u-filter m3u-filter COPY --from=node-build /app/build web -COPY --from=resource-build /src/resources/freeze_frame.ts /freeze_frame.ts +COPY --from=resource-build /src/resources/channel_unavailable.ts /channel_unavailable.ts +COPY --from=resource-build /src/resources/user_connections_exhausted.ts /user_connections_exhausted.ts ENTRYPOINT ["/sbin/tini", "--", "/app/m3u-filter"] CMD ["-s", "-p", "/app/config"] diff --git a/docker/Dockerfile-manual b/docker/Dockerfile-manual index a4730964e..44c4e7d38 100644 --- a/docker/Dockerfile-manual +++ b/docker/Dockerfile-manual @@ -9,7 +9,8 @@ COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY ./m3u-filter / COPY ./web /web -COPY ./freeze_frame.ts ./freeze_frame.ts +COPY ./channel_unavailable.ts ./channel_unavailable.ts +COPY ./user_connections_exhausted.ts ./user_connections_exhausted.ts CMD ["./m3u-filter", "-s", "-p", "/config"] diff --git a/resources/freeze_frame.jpg b/resources/channel_unavailable.jpg similarity index 100% rename from resources/freeze_frame.jpg rename to resources/channel_unavailable.jpg diff --git a/resources/user_connections_exhausted.jpg b/resources/user_connections_exhausted.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3cd0763739111928e7c9dd21ff9fcb169c362893 GIT binary patch literal 232071 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%0D|b1CgQ*eMZued~1H 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%0A{HbD_L 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 literal 0 HcmV?d00001 diff --git a/src/api/api_utils.rs b/src/api/api_utils.rs index ad5cfa00c..69ea68b12 100644 --- a/src/api/api_utils.rs +++ b/src/api/api_utils.rs @@ -120,7 +120,16 @@ pub async fn get_user_target<'a>(api_req: &'a UserApiRequest, app_state: &'a App get_user_target_by_credentials(username, password, api_req, app_state).await } -fn get_stream_options(app_state: &AppState) -> (bool, u32, u32, bool, usize, bool) { +pub struct StreamOptions { + pub stream_retry: bool, + pub stream_force_retry_secs: u32, + pub stream_connect_timeout_secs: u32, + pub buffer_enabled: bool, + pub buffer_size: usize, + pub pipe_provider_stream: bool +} + +fn get_stream_options(app_state: &AppState) -> StreamOptions { let (stream_retry, stream_force_retry_secs, stream_connect_timeout_secs, buffer_enabled, buffer_size) = app_state .config .reverse_proxy @@ -134,7 +143,7 @@ fn get_stream_options(app_state: &AppState) -> (bool, u32, u32, bool, usize, boo (stream.retry, stream.forced_retry_interval_secs, stream.connect_timeout_secs, buffer_enabled, buffer_size) }); let pipe_provider_stream = !stream_retry && !buffer_enabled; - (stream_retry, stream_force_retry_secs, stream_connect_timeout_secs, buffer_enabled, buffer_size, pipe_provider_stream) + StreamOptions { stream_retry, stream_force_retry_secs, stream_connect_timeout_secs, buffer_enabled, buffer_size, pipe_provider_stream} } // fn get_stream_content_length(provider_response: Option<&(Vec<(String, String)>, StatusCode)>) -> u64 { @@ -160,15 +169,14 @@ pub async fn stream_response(app_state: &AppState, stream_url: &str, } } - let (stream_retry, stream_force_retry_secs, stream_connect_timeout, buffer_enabled, buffer_size, direct_pipe_provider_stream) = - get_stream_options(app_state); + let stream_options = get_stream_options(app_state); get_stream_options(app_state); if let Ok(url) = Url::parse(stream_url) { let event_manager = Arc::clone(&app_state.event_manager); - let (stream_opt, provider_response) = if direct_pipe_provider_stream { - provider_stream::get_provider_pipe_stream(&app_state.config, &app_state.http_client, &url, req_headers, input, item_type).await + let (stream_opt, provider_response) = if stream_options.pipe_provider_stream { + provider_stream::get_provider_pipe_stream(&app_state.config, &app_state.http_client, &url, req_headers, input, item_type, &stream_options).await } else { - let buffer_stream_options = BufferStreamOptions::new(item_type, stream_retry, stream_force_retry_secs, stream_connect_timeout, buffer_enabled, buffer_size, share_stream); + let buffer_stream_options = BufferStreamOptions::new(item_type,share_stream, &stream_options); provider_stream::get_provider_reconnect_buffered_stream(&app_state.config, &app_state.http_client, &url, req_headers, input, buffer_stream_options).await }; if let Some(stream) = stream_opt { @@ -176,7 +184,7 @@ pub async fn stream_response(app_state: &AppState, stream_url: &str, let stream = ActiveClientStream::new(stream, event_manager, &user.username, input.map(|c| c.name.clone())).await; let stream_resp = if share_stream { let shared_headers = provider_response.as_ref().map_or_else(Vec::new, |(h, _)| h.clone()); - SharedStreamManager::subscribe(app_state, stream_url, stream, shared_headers, buffer_size).await; + SharedStreamManager::subscribe(app_state, stream_url, stream, shared_headers, stream_options.buffer_size).await; if let Some(broadcast_stream) = SharedStreamManager::subscribe_shared_stream(app_state, stream_url).await { let (status_code, header_map) = get_stream_response_with_headers(provider_response, stream_url); let mut response = axum::response::Response::builder() @@ -352,4 +360,4 @@ pub fn redirect(url: &str) -> impl IntoResponse { .header("Location", url) .body(axum::body::Body::empty()) .unwrap() -} \ No newline at end of file +} diff --git a/src/api/endpoints/hls_api.rs b/src/api/endpoints/hls_api.rs index 88d999587..1387d4611 100644 --- a/src/api/endpoints/hls_api.rs +++ b/src/api/endpoints/hls_api.rs @@ -1,17 +1,15 @@ use crate::api::api_utils::stream_response; use crate::api::api_utils::try_option_bad_request; use crate::api::model::app_state::AppState; -use crate::api::model::streams::provider_stream::create_freeze_frame_stream; +use crate::api::model::streams::provider_stream::{create_custom_video_stream_response, CustomVideoStreamType}; use crate::model::api_proxy::ProxyUserCredentials; use crate::model::config::{ConfigInput, TargetType}; use crate::model::playlist::{PlaylistItemType, XtreamCluster}; use crate::processing::parser::hls::rewrite_hls; -use crate::repository::playlist_repository::HLS_EXT; use crate::utils::network::request; -use crate::utils::network::request::{replace_extension, sanitize_sensitive_info}; +use crate::utils::network::request::{is_hls_url, replace_url_extension, sanitize_sensitive_info, HLS_EXT}; use axum::response::IntoResponse; use log::{debug, error}; -use reqwest::StatusCode; use serde::Deserialize; use std::sync::Arc; @@ -31,7 +29,7 @@ pub(in crate::api) async fn handle_hls_stream_request(app_state: &Arc, virtual_id: u32, input: &ConfigInput, target_type: TargetType) -> impl axum::response::IntoResponse + Send { - let url = replace_extension(hls_url, HLS_EXT); + let url = replace_url_extension(hls_url, HLS_EXT); let server_info = app_state.config.get_user_server_info(user).await; match request::download_text_content(Arc::clone(&app_state.http_client), input, &url, None).await { Ok(content) => { @@ -46,17 +44,7 @@ pub(in crate::api) async fn handle_hls_stream_request(app_state: &Arc, } Err(err) => { error!("Failed to download m3u8 {}", sanitize_sensitive_info(err.to_string().as_str())); - if let Some((stream, (headers, status_code))) - = create_freeze_frame_stream(&app_state.config, &[], StatusCode::BAD_REQUEST) { - let mut builder = axum::response::Response::builder() - .status(status_code); - for (key, value) in headers { - builder = builder.header(key, value); - } - return builder.body(axum::body::Body::from_stream(stream)) - .unwrap().into_response(); - } - axum::http::StatusCode::NO_CONTENT.into_response() + create_custom_video_stream_response(&app_state.config, &CustomVideoStreamType::ChannelUnavailable).into_response() } } } @@ -69,9 +57,13 @@ async fn hls_api_stream( let (user, target) = try_option_bad_request!( app_state.config.get_target_for_user(¶ms.username, ¶ms.password).await, false, format!("Could not find any user {}", params.username)); - if !user.has_permissions(&app_state).await { + if user.permission_denied(&app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } + if user.connections_exhausted(&app_state).await { + return create_custom_video_stream_response(&app_state.config, &CustomVideoStreamType::UserConnectionsExhausted).into_response(); + } + let Some(hls_entry) = app_state.hls_cache.get_entry(¶ms.token).await else { return axum::http::StatusCode::BAD_REQUEST.into_response(); }; let Some(hls_url) = hls_entry.get_chunk_url(params.chunk) else { return axum::http::StatusCode::BAD_REQUEST.into_response(); }; @@ -79,7 +71,7 @@ async fn hls_api_stream( let virtual_id = params.stream_id; let input = try_option_bad_request!(app_state.config.get_input_by_name(&hls_entry.input_name), true, format!("Cant find input for target {target_name}, context {}, stream_id {virtual_id}", XtreamCluster::Live)); - if hls_url.ends_with(HLS_EXT) { + if is_hls_url(hls_url) { return handle_hls_stream_request(&app_state, &user, hls_url, virtual_id, input, hls_entry.target_type.clone()).await.into_response(); } diff --git a/src/api/endpoints/m3u_api.rs b/src/api/endpoints/m3u_api.rs index 18c1649dc..13dc0286e 100644 --- a/src/api/endpoints/m3u_api.rs +++ b/src/api/endpoints/m3u_api.rs @@ -7,14 +7,14 @@ use crate::model::config::TargetType; use crate::model::playlist::{FieldGetAccessor, PlaylistItemType, XtreamCluster}; use crate::repository::m3u_playlist_iterator::{M3U_RESOURCE_PATH, M3U_STREAM_PATH}; use crate::repository::m3u_repository::{m3u_get_item_for_stream_id, m3u_load_rewrite_playlist}; -use crate::repository::playlist_repository::HLS_EXT; -use crate::utils::network::request::{replace_extension, sanitize_sensitive_info}; +use crate::utils::network::request::{replace_url_extension, sanitize_sensitive_info, HLS_EXT}; use crate::utils::debug_if_enabled; use axum::response::IntoResponse; use bytes::Bytes; use futures::stream; use log::{debug, error}; use std::sync::Arc; +use crate::api::model::streams::provider_stream::{create_custom_video_stream_response, CustomVideoStreamType}; async fn m3u_api( api_req: &UserApiRequest, @@ -69,9 +69,12 @@ async fn m3u_api_stream( let virtual_id: u32 = try_result_bad_request!(action_stream_id.trim().parse()); let Some((user, target)) = get_user_target_by_credentials(&username, &password, &api_req, &app_state).await else { return axum::http::StatusCode::BAD_REQUEST.into_response() }; - if !user.has_permissions(&app_state).await { + if user.permission_denied(&app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } + if user.connections_exhausted(&app_state).await { + return create_custom_video_stream_response(&app_state.config, &CustomVideoStreamType::UserConnectionsExhausted).into_response(); + } if !target.has_output(&TargetType::M3u) { return axum::http::StatusCode::BAD_REQUEST.into_response(); @@ -90,7 +93,7 @@ async fn m3u_api_stream( let is_hls_request = m3u_item.item_type == PlaylistItemType::LiveHls || stream_ext.as_deref() == Some(HLS_EXT); if user.proxy == ProxyType::Redirect { - let redirect_url = if is_hls_request { &replace_extension(&m3u_item.url, "m3u8") } else { &m3u_item.url }; + let redirect_url = if is_hls_request { &replace_url_extension(&m3u_item.url, "m3u8") } else { &m3u_item.url }; // TODO alias processing debug_if_enabled!("Redirecting m3u stream request to {}", sanitize_sensitive_info(redirect_url)); return redirect(redirect_url.as_str()).into_response(); @@ -114,7 +117,7 @@ async fn m3u_api_resource( let Ok(m3u_stream_id) = stream_id.parse::() else { return axum::http::StatusCode::BAD_REQUEST.into_response() }; let Some((user, target)) = get_user_target_by_credentials(&username, &password, &api_req, &app_state).await else { return axum::http::StatusCode::BAD_REQUEST.into_response() }; - if !user.has_permissions(&app_state).await { + if user.permission_denied(&app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } diff --git a/src/api/endpoints/user_api.rs b/src/api/endpoints/user_api.rs index 8d20a31fa..9f4151ec2 100644 --- a/src/api/endpoints/user_api.rs +++ b/src/api/endpoints/user_api.rs @@ -47,7 +47,7 @@ async fn playlist_categories( ) -> impl axum::response::IntoResponse + Send { if let Some(username) = get_username_from_auth_header(&token, &app_state) { if let Some((user, target)) = get_user_target_by_username(username.as_str(), &app_state).await { - if !user.has_permissions(&app_state).await { + if user.permission_denied(&app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } let config = &app_state.config; @@ -105,7 +105,7 @@ async fn save_playlist_bouquet( ) -> impl axum::response::IntoResponse + Send { if let Some(username) = get_username_from_auth_header(&token, &app_state) { if let Some((user, target)) = get_user_target_by_username(username.as_str(), &app_state).await { - if !user.has_permissions(&app_state).await { + if user.permission_denied(&app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } match save_user_bouquet(&app_state.config, &target.name, &username, &bouquet).await { @@ -127,7 +127,7 @@ async fn playlist_bouquet( ) -> impl axum::response::IntoResponse + Send { if let Some(username) = get_username_from_auth_header(&token, &app_state) { if let Some((user, _target)) = get_user_target_by_username(username.as_str(), &app_state).await { - if !user.has_permissions(&app_state).await { + if user.permission_denied(&app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } let xtream = load_user_bouquet_as_json(&app_state.config, &username, TargetType::Xtream).await; diff --git a/src/api/endpoints/xmltv_api.rs b/src/api/endpoints/xmltv_api.rs index a71e359fa..2f762bf5a 100644 --- a/src/api/endpoints/xmltv_api.rs +++ b/src/api/endpoints/xmltv_api.rs @@ -175,7 +175,7 @@ async fn xmltv_api( axum::extract::State(app_state): axum::extract::State>, ) -> impl axum::response::IntoResponse + Send { if let Some((user, target)) = get_user_target(&api_req, &app_state).await { - if !user.has_permissions(&app_state).await { + if user.permission_denied(&app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } match get_epg_path_for_target(&app_state.config, target) { diff --git a/src/api/endpoints/xtream_api.rs b/src/api/endpoints/xtream_api.rs index 88a5b6e26..4ecd45a70 100644 --- a/src/api/endpoints/xtream_api.rs +++ b/src/api/endpoints/xtream_api.rs @@ -21,6 +21,7 @@ use crate::api::endpoints::hls_api::handle_hls_stream_request; use crate::api::endpoints::xmltv_api::get_empty_epg_response; use crate::api::model::app_state::AppState; use crate::api::model::request::UserApiRequest; +use crate::api::model::streams::provider_stream::{create_custom_video_stream_response, CustomVideoStreamType}; use crate::api::model::xtream::XtreamAuthorizationResponse; use crate::m3u_filter_error::{str_to_io_error, M3uFilterError, M3uFilterErrorKind}; use crate::model::api_proxy::{ProxyType, ProxyUserCredentials}; @@ -28,13 +29,13 @@ use crate::model::config::TargetType; use crate::model::config::{Config, ConfigInput, ConfigTarget}; use crate::model::playlist::{get_backdrop_path_value, FieldGetAccessor, PlaylistEntry, PlaylistItemType, XtreamCluster, XtreamPlaylistItem}; use crate::model::xtream::{INFO_RESOURCE_PREFIX, INFO_RESOURCE_PREFIX_EPISODE, PROP_BACKDROP_PATH, SEASON_RESOURCE_PREFIX}; -use crate::repository::playlist_repository::{get_target_id_mapping, HLS_EXT}; +use crate::repository::playlist_repository::{get_target_id_mapping}; use crate::repository::storage::{get_target_storage_path, hex_encode}; use crate::repository::{user_repository, xtream_repository}; use crate::repository::xtream_repository::{TAG_EPISODES, TAG_INFO_DATA, TAG_SEASONS_DATA}; use crate::utils::hash_utils::generate_playlist_uuid; use crate::utils::json_utils::get_u32_from_serde_value; -use crate::utils::network::request::{extract_extension_from_url, sanitize_sensitive_info}; +use crate::utils::network::request::{extract_extension_from_url, sanitize_sensitive_info, HLS_EXT}; use crate::utils::network::xtream::{create_vod_info_from_item, ACTION_GET_LIVE_CATEGORIES, ACTION_GET_LIVE_STREAMS, ACTION_GET_SERIES, ACTION_GET_SERIES_CATEGORIES, ACTION_GET_SERIES_INFO, ACTION_GET_VOD_CATEGORIES, ACTION_GET_VOD_INFO, ACTION_GET_VOD_STREAMS}; use crate::utils::json_utils; use crate::utils::debug_if_enabled; @@ -146,9 +147,13 @@ async fn xtream_player_api_stream( stream_req: XtreamApiStreamRequest<'_>, ) -> impl axum::response::IntoResponse + Send { let (user, target) = try_option_bad_request!(get_user_target_by_credentials(stream_req.username, stream_req.password, api_req, app_state).await, false, format!("Could not find any user {}", stream_req.username)); - if !user.has_permissions(app_state).await { + if user.permission_denied(app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } + if user.connections_exhausted(app_state).await { + return create_custom_video_stream_response(&app_state.config, &CustomVideoStreamType::UserConnectionsExhausted).into_response(); + } + let target_name = &target.name; if !target.has_output(&TargetType::Xtream) { debug!("Target has no xtream output {}", target_name); @@ -337,7 +342,7 @@ async fn xtream_player_api_resource( resource_req: XtreamApiStreamRequest<'_>, ) -> impl axum::response::IntoResponse { let (user, target) = try_option_bad_request!(get_user_target_by_credentials(resource_req.username, resource_req.password, api_req, app_state).await, false, format!("Could not find any user {}", resource_req.username)); - if !user.has_permissions(app_state).await { + if user.permission_denied(app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } let target_name = &target.name; @@ -617,7 +622,7 @@ async fn xtream_player_api( return axum::response::Json(get_user_info(&user, app_state).await).into_response(); } - if !user.has_permissions(app_state).await { + if user.permission_denied(app_state) { return axum::http::StatusCode::FORBIDDEN.into_response(); } @@ -719,24 +724,13 @@ async fn xtream_player_api_get( xtream_player_api(api_req, &app_state).await } -// async fn xtream_player_api_get(req: axum::http::Request, -// axum::extract::Query(api_req): axum::extract::Query, -// axum::extract::State(app_state): axum::extract::State>, -// ) -> impl axum::response::IntoResponse + Send { -// xtream_player_api(&req, api_req.into_inner(), &app_state).await -// } + async fn xtream_player_api_post( axum::extract::State(app_state): axum::extract::State>, axum::extract::Form(api_req): axum::extract::Form, ) -> impl axum::response::IntoResponse + Send { xtream_player_api(api_req, &app_state).await } -// async fn xtream_player_api_post(req: axum::http::Request, -// api_req: web::Form, -// axum::extract::State(app_state): axum::extract::State>, -// ) -> impl axum::response::IntoResponse + Send { -// xtream_player_api(&req, api_req.into_inner(), &app_state).await -// } macro_rules! register_xtream_api { ($router:expr, [$($path:expr),*]) => {{ diff --git a/src/api/model/active_provider_manager.rs b/src/api/model/active_provider_manager.rs index 55e3e500f..3a8d22905 100644 --- a/src/api/model/active_provider_manager.rs +++ b/src/api/model/active_provider_manager.rs @@ -648,7 +648,7 @@ mod tests { assert!(lineup.acquire(false).is_some()); // Release one connection - lineup.release(1); + lineup.release("provider7_1"); // After release, one connection should be available assert!(lineup.acquire(false).is_some()); @@ -721,7 +721,7 @@ mod tests { } // Verify that only the capacity of the provider was utilized (2 connections) - assert_eq!(*lineup.provider.current_connections.read(), 2); + assert_eq!(lineup.provider.current_connections.load(Ordering::SeqCst), 2); } } diff --git a/src/api/model/streams/freeze_frame_stream.rs b/src/api/model/streams/custom_video_stream.rs similarity index 93% rename from src/api/model/streams/freeze_frame_stream.rs rename to src/api/model/streams/custom_video_stream.rs index 8b94a1359..4c51dc11d 100644 --- a/src/api/model/streams/freeze_frame_stream.rs +++ b/src/api/model/streams/custom_video_stream.rs @@ -8,13 +8,13 @@ use std::task::{Context, Poll}; const CHUNK_SIZE: usize = 8192; -pub struct FreezeFrameStream { +pub struct CustomVideoStream { buffer: Arc>, buffer_len: usize, current_pos: usize, // Keep track of the current position in the buffer } -impl FreezeFrameStream { +impl CustomVideoStream { pub fn new(buffer: Arc>) -> Self { let buffer_len = buffer.len(); Self { @@ -25,7 +25,7 @@ impl FreezeFrameStream { } } -impl Stream for FreezeFrameStream { +impl Stream for CustomVideoStream { type Item = Result; fn poll_next( diff --git a/src/api/model/streams/mod.rs b/src/api/model/streams/mod.rs index 886de44e4..1edab8c5c 100644 --- a/src/api/model/streams/mod.rs +++ b/src/api/model/streams/mod.rs @@ -6,4 +6,4 @@ pub(in crate::api) mod active_client_stream; mod timed_client_stream; mod buffered_stream; mod client_stream; -mod freeze_frame_stream; \ No newline at end of file +mod custom_video_stream; \ No newline at end of file diff --git a/src/api/model/streams/provider_stream.rs b/src/api/model/streams/provider_stream.rs index 2e3079a65..e0d102bb9 100644 --- a/src/api/model/streams/provider_stream.rs +++ b/src/api/model/streams/provider_stream.rs @@ -1,7 +1,7 @@ -use crate::api::api_utils::{get_headers_from_request, HeaderFilter}; +use crate::api::api_utils::{get_headers_from_request, HeaderFilter, StreamOptions}; use crate::api::model::model_utils::get_response_headers; use crate::api::model::stream_error::StreamError; -use crate::api::model::streams::freeze_frame_stream::FreezeFrameStream; +use crate::api::model::streams::custom_video_stream::CustomVideoStream; use crate::api::model::streams::provider_stream_factory::{create_provider_stream, BufferStreamOptions}; use crate::model::config::{Config, ConfigInput}; use crate::model::playlist::PlaylistItemType; @@ -13,26 +13,63 @@ use futures::TryStreamExt; use log::{debug, error}; use reqwest::StatusCode; use std::sync::Arc; +use std::time::Duration; use axum::http::HeaderMap; +use axum::response::IntoResponse; use url::Url; type BoxedProviderStream = BoxStream<'static, Result>; type ProviderStreamHeader = Vec<(String, String)>; type ProviderStreamResponse = (Option, Option<(ProviderStreamHeader, StatusCode)>); -pub fn create_freeze_frame_stream(cfg: &Config, headers: &[(String, String)], status: StatusCode ) -> Option<(BoxedProviderStream, (ProviderStreamHeader, StatusCode))> { - if let Some(freeze_frame) = cfg.t_channel_unavailable_file.as_ref() { - debug!("Streaming response freeze frame for status {status}"); +pub enum CustomVideoStreamType { + ChannelUnavailable, + UserConnectionsExhausted, + ProviderConnectionsExhausted, +} + +fn create_video_stream(video: Option<&Arc>>, headers: &[(String, String)], log_message: &str) -> Option<(BoxedProviderStream, (ProviderStreamHeader, StatusCode))> { + if let Some(video) = video { + debug!("{}", log_message); let mut response_headers: Vec<(String, String)> = headers.iter() .filter(|(key, _)| !(key.eq("content-type") || key.eq("content-length") || key.contains("range"))) .map(|(key, value)| (key.to_string(), value.to_string())).collect(); response_headers.push(("content-type".to_string(), "video/mp2t".to_string())); - Some((Box::pin(FreezeFrameStream::new(Arc::clone(freeze_frame))), (response_headers, StatusCode::OK))) - } else { + Some((Box::pin(CustomVideoStream::new(Arc::clone(video))), (response_headers, StatusCode::OK))) + } else { None } } +pub fn create_channel_unavailable_stream(cfg: &Config, headers: &[(String, String)], status: StatusCode) -> Option<(BoxedProviderStream, (ProviderStreamHeader, StatusCode))> { + create_video_stream(cfg.t_channel_unavailable_video.as_ref(), headers, &format!("Streaming response channel unavailable for status {status}")) +} + +pub fn create_user_connections_exhausted_stream(cfg: &Config, headers: &[(String, String)]) -> Option<(BoxedProviderStream, (ProviderStreamHeader, StatusCode))> { + create_video_stream(cfg.t_user_connections_exhausted_video.as_ref(), headers, "Streaming response user connections exhausted") +} + +pub fn create_provider_connections_exhausted_stream(cfg: &Config, headers: &[(String, String)]) -> Option<(BoxedProviderStream, (ProviderStreamHeader, StatusCode))> { + create_video_stream(cfg.t_provider_connections_exhausted_video.as_ref(), headers, "Streaming response provider connections exhausted") +} + +pub fn create_custom_video_stream_response(config: &Config, video_response: &CustomVideoStreamType) -> impl axum::response::IntoResponse + Send { + if let Some((stream, (headers, status_code))) = match video_response { + CustomVideoStreamType::ChannelUnavailable => create_channel_unavailable_stream(config, &[], StatusCode::BAD_REQUEST), + CustomVideoStreamType::UserConnectionsExhausted => create_user_connections_exhausted_stream(config, &[]), + CustomVideoStreamType::ProviderConnectionsExhausted => create_provider_connections_exhausted_stream(config, &[]), + } { + let mut builder = axum::response::Response::builder() + .status(status_code); + for (key, value) in headers { + builder = builder.header(key, value); + } + return builder.body(axum::body::Body::from_stream(stream)).unwrap().into_response(); + } + axum::http::StatusCode::FORBIDDEN.into_response() +} + + pub fn get_header_filter_for_item_type(item_type: PlaylistItemType) -> HeaderFilter { match item_type { PlaylistItemType::Live | PlaylistItemType::LiveUnknown | PlaylistItemType::LiveHls => { @@ -47,7 +84,8 @@ pub async fn get_provider_pipe_stream(cfg: &Config, stream_url: &Url, req_headers: &HeaderMap, input: Option<&ConfigInput>, - item_type: PlaylistItemType) -> ProviderStreamResponse { + item_type: PlaylistItemType, + stream_options: &StreamOptions) -> ProviderStreamResponse { let filter_header = get_header_filter_for_item_type(item_type); let req_headers = get_headers_from_request(req_headers, &filter_header); debug_if_enabled!("Stream requested with headers: {:?}", req_headers.iter().map(|header| (header.0, String::from_utf8_lossy(header.1))).collect::>()); @@ -56,14 +94,20 @@ pub async fn get_provider_pipe_stream(cfg: &Config, // The stream url, we need to clone it because of move to async block. // We merge configured input headers with the headers from the request. let headers = get_request_headers(input_headers.as_ref(), Some(&req_headers)); - let client = http_client.get(stream_url.clone()).headers(headers.clone()); + let client_builder = http_client.get(stream_url.clone()).headers(headers.clone()); + let client = if stream_options.stream_connect_timeout_secs > 0 { + client_builder.timeout(Duration::from_secs(u64::from(stream_options.stream_connect_timeout_secs))) + } else { + client_builder + }; match client.send().await { Ok(response) => { let response_headers = get_response_headers(response.headers()); + // TODO hls handling let status = response.status(); if status.is_success() { (Some(Box::pin(response.bytes_stream().map_err(|err| StreamError::reqwest(&err)))), Some((response_headers, status))) - } else if let Some((boxed_provider_stream, response_info)) = create_freeze_frame_stream(cfg, &response_headers, status) { + } else if let Some((boxed_provider_stream, response_info)) = create_channel_unavailable_stream(cfg, &response_headers, status) { (Some(boxed_provider_stream), Some(response_info)) } else { (None, Some((response_headers, status))) @@ -72,7 +116,7 @@ pub async fn get_provider_pipe_stream(cfg: &Config, Err(err) => { let masked_url = sanitize_sensitive_info(stream_url.as_str()); error!("Failed to open stream {masked_url} {err}"); - if let Some((boxed_provider_stream, response_info)) = create_freeze_frame_stream(cfg, &get_response_headers(&headers), StatusCode::BAD_GATEWAY) { + if let Some((boxed_provider_stream, response_info)) = create_channel_unavailable_stream(cfg, &get_response_headers(&headers), StatusCode::BAD_GATEWAY) { (Some(boxed_provider_stream), Some(response_info)) } else { (None, None) diff --git a/src/api/model/streams/provider_stream_factory.rs b/src/api/model/streams/provider_stream_factory.rs index 8d2a35e56..7c1d3f1f0 100644 --- a/src/api/model/streams/provider_stream_factory.rs +++ b/src/api/model/streams/provider_stream_factory.rs @@ -1,9 +1,10 @@ -use crate::api::api_utils::get_headers_from_request; +use crate::api::api_utils::{get_headers_from_request, StreamOptions}; use crate::api::model::model_utils::get_response_headers; use crate::api::model::stream_error::StreamError; use crate::api::model::streams::buffered_stream::BufferedStream; use crate::api::model::streams::client_stream::ClientStream; -use crate::api::model::streams::provider_stream::{create_freeze_frame_stream, get_header_filter_for_item_type}; +use crate::api::model::streams::provider_stream::{create_channel_unavailable_stream, get_header_filter_for_item_type}; +use crate::api::model::streams::timed_client_stream::{TimeoutClientStream}; use crate::model::config::{Config, ConfigInput}; use crate::model::playlist::PlaylistItemType; use crate::tools::atomic_once_flag::AtomicOnceFlag; @@ -20,7 +21,6 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; use url::Url; -use crate::api::model::streams::timed_client_stream::TimedClientStream; // TODO make this configurable pub const STREAM_QUEUE_SIZE: usize = 4096; // mpsc channel holding messages. with possible 8092byte chunks @@ -43,20 +43,16 @@ pub struct BufferStreamOptions { impl BufferStreamOptions { pub(crate) fn new( item_type: PlaylistItemType, - reconnect_enabled: bool, - force_reconnect_secs: u32, - connect_timeout: u32, - buffer_enabled: bool, - buffer_size: usize, - share_stream: bool + share_stream: bool, + stream_options: &StreamOptions ) -> Self { Self { item_type, - reconnect_enabled, - force_reconnect_secs, - connect_timeout_secs: connect_timeout, - buffer_enabled, - buffer_size, + reconnect_enabled: stream_options.stream_retry, + force_reconnect_secs: stream_options.stream_force_retry_secs, + connect_timeout_secs: stream_options.stream_connect_timeout_secs, + buffer_enabled: stream_options.buffer_enabled, + buffer_size: stream_options.buffer_size, share_stream, } } @@ -197,33 +193,44 @@ fn get_client_stream_request_params( (stream_buffer_size, req_range_start_bytes, options.is_reconnect_enabled(), options.force_reconnect_secs, options.connect_timeout_secs, headers) } -fn prepare_client(request_client: &Arc, url: &Url, headers: &HeaderMap, range_start_bytes_to_request: Option) -> (reqwest::RequestBuilder, bool) { +fn prepare_client(request_client: &Arc, stream_options: &ProviderStreamOptions) -> (reqwest::RequestBuilder, bool) { + let url = stream_options.get_url(); + let range_start = stream_options.get_total_bytes_send(); + let headers = stream_options.get_headers(); let mut client = request_client.get(url.clone()).headers(headers.clone()); - if let Some(range) = range_start_bytes_to_request { - // on reconnect send range header to avoid starting from beginning for vod - let range = format!("bytes={range}-", ); - client = client.header(RANGE, range); - (client, true) // partial content + + let (client_builder, partial) = { + if let Some(range) = range_start { + // on reconnect send range header to avoid starting from beginning for vod + let range = format!("bytes={range}-", ); + client = client.header(RANGE, range); + (client, true) // partial content + } else { + (client, false) + } + }; + + let client = if stream_options.connect_timeout_secs > 0 { + client_builder.timeout(Duration::from_secs(u64::from(stream_options.connect_timeout_secs))) } else { - (client, false) - } + client_builder + }; + (client, partial) } -async fn provider_request(cfg: &Config, request_client: Arc, initial_info: bool, stream_options: &ProviderStreamOptions) -> Result, StatusCode> { - let (client, _partial_content) = prepare_client(&request_client, stream_options.get_url(), stream_options.get_headers(), stream_options.get_total_bytes_send()); +async fn provider_initial_request(cfg: &Config, request_client: Arc, stream_options: &ProviderStreamOptions) -> Result, StatusCode> { + let (client, _partial_content) = prepare_client(&request_client, stream_options); match client.send().await { Ok(mut response) => { let status = response.status(); if status.is_success() { - let response_info = if initial_info { + let response_info = { // Unfortunately, the HEAD request does not work, so we need this workaround. // We need some header information from the provider, we extract the necessary headers and forward them to the client debug_if_enabled!("Provider response status: '{}' headers: {:?}", response.status(), response.headers_mut()); let response_headers: Vec<(String, String)> = get_response_headers(response.headers()); // debug!("First headers {headers:?} {} {}", sanitize_sensitive_info(url.as_str())); Some((response_headers, response.status())) - } else { - None }; return Ok(Some((response.bytes_stream().map_err(|err| { error!("Failed to read response body: {err}"); @@ -231,7 +238,7 @@ async fn provider_request(cfg: &Config, request_client: Arc, in }).boxed(), response_info))); } if let Some((boxed_provider_stream, response_info)) = - create_freeze_frame_stream(cfg, &get_response_headers(response.headers()), status) + create_channel_unavailable_stream(cfg, &get_response_headers(response.headers()), status) { return Ok(Some((boxed_provider_stream, Some(response_info)))); } @@ -239,7 +246,7 @@ async fn provider_request(cfg: &Config, request_client: Arc, in } Err(_err) => { if let Some((boxed_provider_stream, response_info)) = - create_freeze_frame_stream(cfg, &get_response_headers(stream_options.get_headers()), StatusCode::BAD_GATEWAY) + create_channel_unavailable_stream(cfg, &get_response_headers(stream_options.get_headers()), StatusCode::BAD_GATEWAY) { Ok(Some((boxed_provider_stream, Some(response_info)))) } else { @@ -252,17 +259,10 @@ async fn provider_request(cfg: &Config, request_client: Arc, in async fn stream_provider(client: Arc, stream_options: ProviderStreamOptions) -> Option { let url = stream_options.get_url(); - let range_start = stream_options.get_total_bytes_send(); - let headers = stream_options.get_headers(); debug_if_enabled!("stream provider {}", sanitize_sensitive_info(url.as_str())); while stream_options.should_continue() { debug_if_enabled!("Reconnecting stream {}", sanitize_sensitive_info(url.as_str())); - let (client_builder, _) = prepare_client(&client, url, headers, range_start); - let client = if stream_options.connect_timeout_secs > 0 { - client_builder.timeout(Duration::from_secs(u64::from(stream_options.connect_timeout_secs))) - } else { - client_builder - }; + let (client, _) = prepare_client(&client, &stream_options); match client.send().await { Ok(response) => { let status = response.status(); @@ -272,10 +272,10 @@ async fn stream_provider(client: Arc, stream_options: ProviderS StreamError::reqwest(&err) }).boxed(); return if stream_options.reconnect_force_secs > 0 { - Some(TimedClientStream::new(provider_stream, stream_options.reconnect_force_secs).boxed()) + Some(TimeoutClientStream::new(provider_stream, stream_options.reconnect_force_secs).boxed()) } else { Some(provider_stream) - } + }; } if status.is_client_error() { return None; @@ -307,7 +307,7 @@ async fn get_initial_stream(cfg: &Config, client: Arc, stream_o let start = Instant::now(); let mut connect_err: u32 = 1; while stream_options.should_continue() { - match provider_request(cfg, Arc::clone(&client), true, stream_options).await { + match provider_initial_request(cfg, Arc::clone(&client), stream_options).await { Ok(Some(value)) => return Some(value), Ok(None) => { if connect_err > ERR_MAX_RETRY_COUNT { @@ -338,7 +338,7 @@ fn create_provider_stream_options(stream_url: &Url, req_headers: &HeaderMap, input: Option<&ConfigInput>, options: &BufferStreamOptions) -> ProviderStreamOptions { - let (buffer_size, req_range_start_bytes, reconnect, reconnect_force_secs, connect_timeout_secs, headers) + let (buffer_size, req_range_start_bytes, reconnect, reconnect_force_secs, connect_timeout_secs, headers) = get_client_stream_request_params(req_headers, input, options); let url = stream_url.clone(); let range_bytes = Arc::new(req_range_start_bytes.map(AtomicUsize::new)); diff --git a/src/api/model/streams/timed_client_stream.rs b/src/api/model/streams/timed_client_stream.rs index 8d98c4339..0195dcb86 100644 --- a/src/api/model/streams/timed_client_stream.rs +++ b/src/api/model/streams/timed_client_stream.rs @@ -6,18 +6,18 @@ use std::pin::Pin; use std::task::Poll; use std::time::{Duration, Instant}; -pub struct TimedClientStream { +pub struct TimeoutClientStream { inner: ResponseStream, duration: Duration, start_time: Instant, } -impl TimedClientStream { +impl TimeoutClientStream { pub(crate) fn new(inner: ResponseStream, duration: u32) -> Self { Self { inner, duration: Duration::from_secs(u64::from(duration)) , start_time: Instant::now() } } } -impl Stream for TimedClientStream { +impl Stream for TimeoutClientStream { type Item = Result; fn poll_next(mut self: Pin<&mut Self>,cx: &mut std::task::Context<'_>,) -> Poll> { diff --git a/src/api/scheduler.rs b/src/api/scheduler.rs index 8f1846286..bac5c69ae 100644 --- a/src/api/scheduler.rs +++ b/src/api/scheduler.rs @@ -44,7 +44,6 @@ pub async fn start_scheduler(client: Arc, expression: &str, con mod tests { use std::str::FromStr; use std::sync::atomic::{AtomicU8, Ordering}; - use std::time::Instant; use chrono::Local; use cron::Schedule; use crate::api::scheduler::datetime_to_instant; diff --git a/src/foundation/filter.rs b/src/foundation/filter.rs index 3fc7128f9..ac4d5c8d3 100644 --- a/src/foundation/filter.rs +++ b/src/foundation/filter.rs @@ -626,24 +626,24 @@ mod tests { assert_eq!(filtered.len(), 2); assert_eq!( filtered.iter().any(|&chan| { - let group = chan.header.borrow().group.to_string(); - let name = chan.header.borrow().name.to_string(); + let group = chan.header.group.to_string(); + let name = chan.header.name.to_string(); name.eq("24/7: Cars") && group.eq("FR Channels") }), true ); assert_eq!( filtered.iter().any(|&chan| { - let group = chan.header.borrow().group.to_string(); - let name = chan.header.borrow().name.to_string(); + let group = chan.header.group.to_string(); + let name = chan.header.name.to_string(); name.eq("Entertainment") && group.eq("US Channels") }), true ); assert_eq!( filtered.iter().any(|&chan| { - let group = chan.header.borrow().group.to_string(); - let name = chan.header.borrow().name.to_string(); + let group = chan.header.group.to_string(); + let name = chan.header.name.to_string(); name.eq("24/7: Cars") && group.eq("US Channels") }), false diff --git a/src/main.rs b/src/main.rs index 4f2a71149..5895a0168 100644 --- a/src/main.rs +++ b/src/main.rs @@ -145,9 +145,9 @@ fn main() { Err(err) => exit!("{err}"), } - if cfg.t_channel_unavailable_file.is_some() { - info!("Freeze frame video loaded from {:?}", cfg.channel_unavailable_file.as_ref().map_or("?", |v| v.as_str())); - } + // if cfg.t_channel_unavailable_video.is_some() { + // info!("Channel unavailable video loaded from {:?}", cfg.channel_unavailable_file.as_ref().map_or("?", |v| v.as_str())); + // } let rt = tokio::runtime::Runtime::new().unwrap(); let () = rt.block_on(async { diff --git a/src/model/api_proxy.rs b/src/model/api_proxy.rs index 11ef8bf5b..a7f441da8 100644 --- a/src/model/api_proxy.rs +++ b/src/model/api_proxy.rs @@ -171,7 +171,7 @@ impl ProxyUserCredentials { Ok(()) } - pub async fn has_permissions(&self, app_state: &AppState) -> bool { + pub fn has_permissions(&self, app_state: &AppState) -> bool { if app_state.config.user_access_control { if let Some(exp_date) = self.exp_date.as_ref() { let now = Local::now(); @@ -180,12 +180,13 @@ impl ProxyUserCredentials { return false; } } - if let Some(max_connections) = self.max_connections.as_ref() { - if *max_connections < app_state.get_active_connections_for_user(&self.username).await { - debug!("User access denied, too many connections: {}", self.username); - return false; - } - } + // we allow requests with max connection reached, but we should block streaming + // if let Some(max_connections) = self.max_connections.as_ref() { + // if *max_connections < app_state.get_active_connections_for_user(&self.username).await { + // debug!("User access denied, too many connections: {}", self.username); + // return false; + // } + // } if let Some(status) = &self.status { if !matches!(status, ProxyUserStatus::Active | ProxyUserStatus::Trial) { debug!("User access denied, status invalid: {status} for user: {}", self.username); @@ -195,6 +196,29 @@ impl ProxyUserCredentials { } true } + + #[inline] + pub fn permission_denied(&self, app_state: &AppState) -> bool { + !self.has_permissions(app_state) + } + + + pub async fn has_connections_left(&self, app_state: &AppState) -> bool { + if app_state.config.user_access_control { + if let Some(max_connections) = self.max_connections.as_ref() { + if *max_connections < app_state.get_active_connections_for_user(&self.username).await { + debug!("User access denied, too many connections: {}", self.username); + return false; + } + } + } + true + } + + #[inline] + pub async fn connections_exhausted(&self, app_state: &AppState) -> bool { + !self.has_connections_left(app_state).await + } } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/src/model/config.rs b/src/model/config.rs index e065c473c..a6885f48f 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -1152,6 +1152,17 @@ impl ReverseProxyConfig { } } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct CustomStreamResponseConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel_unavailable: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_connection_exhausted: Option, // user has no more connections + #[serde(default, skip_serializing_if = "Option::is_none")] + pub provider_connection_exhausted: Option, // provider limit reached, has no more connections +} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct Config { @@ -1165,7 +1176,7 @@ pub struct Config { #[serde(default, skip_serializing_if = "Option::is_none")] pub user_config_dir: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub channel_unavailable_file: Option, + pub custom_stream_response: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub templates: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -1201,7 +1212,11 @@ pub struct Config { #[serde(skip)] pub file_locks: Arc, #[serde(skip)] - pub t_channel_unavailable_file: Option>>, + pub t_channel_unavailable_video: Option>>, + #[serde(skip)] + pub t_user_connections_exhausted_video: Option>>, + #[serde(skip)] + pub t_provider_connections_exhausted_video: Option>>, } impl Config { @@ -1417,54 +1432,72 @@ impl Config { pub fn prepare(&mut self, resolve_var: bool) -> Result<(), M3uFilterError> { let work_dir = if resolve_var { &config_reader::resolve_env_var(&self.working_dir) } else { &self.working_dir }; self.working_dir = file_utils::get_working_path(work_dir); - - if let Some(channel_unavailable_file) = &self.channel_unavailable_file { - let channel_unavailable = file_utils::make_absolute_path(channel_unavailable_file, &self.working_dir, resolve_var); - match file_utils::read_file_as_bytes(&PathBuf::from(&channel_unavailable)) { - Ok(data) => { - self.t_channel_unavailable_file = Some(Arc::new(data)); - } - Err(err) => { - error!("Failed to load channel unavailable file: {channel_unavailable} {err}"); - } - } - self.channel_unavailable_file = Some(channel_unavailable); - } - - if self.backup_dir.is_none() { - self.backup_dir = Some(PathBuf::from(&self.working_dir).join("backup").clean().to_string_lossy().to_string()); - } else { - self.backup_dir = self.backup_dir.as_ref().map(|backup_dir| { - if resolve_var { - config_reader::resolve_env_var(backup_dir) - } else { - backup_dir.to_owned() - } - }).map(|dir| dir.to_string()); - } - if self.user_config_dir.is_none() { - self.user_config_dir = Some(PathBuf::from(&self.working_dir).join("user_config").clean().to_string_lossy().to_string()); - } else { - self.user_config_dir = self.user_config_dir.as_ref().map(|user_config_dir| { - if resolve_var { - config_reader::resolve_env_var(user_config_dir) - } else { - user_config_dir.to_owned() - } - }).map(|dir| dir.to_string()); - } + self.prepare_custom_stream_response(resolve_var); + self.prepare_directories(resolve_var); if let Some(reverse_proxy) = self.reverse_proxy.as_mut() { reverse_proxy.prepare(&self.working_dir, resolve_var); } + self.prepare_hdhomerun()?; + self.api.prepare(); + self.prepare_api_web_root(resolve_var); + self.prepare_templates()?; + self.check_unique_input_names()?; + let target_names = self.check_unique_target_names()?; + self.check_scheduled_targets(&target_names)?; + self.prepare_sources()?; + self.prepare_video_config()?; + self.prepare_web_auth(resolve_var)?; + Ok(()) + } + + fn prepare_directories(&mut self, resolve_var: bool) { + fn set_directory(path: &mut Option, default_subdir: &str, working_dir: &str, resolve_var: bool) { + *path = Some(match path.as_ref() { + Some(existing) => { + if resolve_var { + resolve_env_var(existing) + } else { + existing.to_owned() + } + } + None => PathBuf::from(working_dir).join(default_subdir).clean().to_string_lossy().to_string(), + }); + } + + set_directory(&mut self.backup_dir, "backup", &self.working_dir, resolve_var); + set_directory(&mut self.user_config_dir, "user_config", &self.working_dir, resolve_var); + } + + fn prepare_hdhomerun(&mut self) -> Result<(), M3uFilterError> { if let Some(hdhomerun) = self.hdhomerun.as_mut() { if hdhomerun.enabled { hdhomerun.prepare(self.api.port)?; } } + Ok(()) + } - self.api.prepare(); - self.prepare_api_web_root(resolve_var); + fn prepare_sources(&mut self) -> Result<(), M3uFilterError> { + // prepare sources and set id's + let mut source_index: u16 = 1; + let mut target_index: u16 = 1; + for source in &mut self.sources { + source_index = source.prepare(source_index)?; + for target in &mut source.targets { + // prepare target templates + let prepare_result = match &self.templates { + Some(templ) => target.prepare(target_index, Some(templ)), + _ => target.prepare(target_index, None) + }; + prepare_result?; + target_index += 1; + } + } + Ok(()) + } + + fn prepare_templates(&mut self) -> Result<(), M3uFilterError> { if let Some(templates) = &mut self.templates { match prepare_templates(templates) { Ok(tmplts) => { @@ -1475,25 +1508,25 @@ impl Config { } } }; - // prepare sources and set id's - let mut source_index: u16 = 1; - let mut target_index: u16 = 1; - self.check_unique_input_names()?; - let target_names = self.check_unique_target_names()?; - self.check_scheduled_targets(&target_names)?; - for source in &mut self.sources { - source_index = source.prepare(source_index)?; - for target in &mut source.targets { - // prepare templates - let prepare_result = match &self.templates { - Some(templ) => target.prepare(target_index, Some(templ)), - _ => target.prepare(target_index, None) - }; - prepare_result?; - target_index += 1; - } + Ok(()) + } + + fn prepare_web_auth(&mut self, resolve_var: bool) -> Result<(), M3uFilterError> { + if !self.web_ui_enabled { + self.web_auth = None; } + if let Some(web_auth) = &mut self.web_auth { + if web_auth.enabled { + web_auth.prepare(&self.t_config_path, resolve_var)?; + } else { + self.web_auth = None; + } + } + Ok(()) + } + + fn prepare_video_config(&mut self) -> Result<(), M3uFilterError> { match &mut self.video { None => { self.video = Some(VideoConfig { @@ -1509,22 +1542,29 @@ impl Config { } } }; - - if !self.web_ui_enabled { - self.web_auth = None; - } - - if let Some(web_auth) = &mut self.web_auth { - if web_auth.enabled { - web_auth.prepare(&self.t_config_path, resolve_var)?; - } else { - self.web_auth = None; - } - } - Ok(()) } + fn prepare_custom_stream_response(&mut self, resolve_var: bool) { + if let Some(custom_stream_response) = self.custom_stream_response.as_ref() { + fn load_and_set_file(path: Option<&String>, working_dir: &str, resolve_var: bool) -> Option>> { + path.as_ref() + .map(|file| file_utils::make_absolute_path(file, working_dir, resolve_var)) + .and_then(|absolute_path| match file_utils::read_file_as_bytes(&PathBuf::from(&absolute_path)) { + Ok(data) => Some(Arc::new(data)), + Err(err) => { + error!("Failed to load file: {absolute_path} {err}"); + None + } + }) + } + + self.t_channel_unavailable_video = load_and_set_file(custom_stream_response.channel_unavailable.as_ref(), &self.working_dir, resolve_var); + self.t_user_connections_exhausted_video = load_and_set_file(custom_stream_response.user_connection_exhausted.as_ref(), &self.working_dir, resolve_var); + self.t_provider_connections_exhausted_video = load_and_set_file(custom_stream_response.provider_connection_exhausted.as_ref(), &self.working_dir, resolve_var); + } + } + fn prepare_api_web_root(&mut self, resolve_var: bool) { if !self.api.web_root.is_empty() { self.api.web_root = file_utils::make_absolute_path(&self.api.web_root, &self.working_dir, resolve_var); diff --git a/src/processing/parser/xmltv.rs b/src/processing/parser/xmltv.rs index 8eb76a568..35d9433b8 100644 --- a/src/processing/parser/xmltv.rs +++ b/src/processing/parser/xmltv.rs @@ -170,8 +170,6 @@ mod tests { use std::collections::HashSet; use std::io; use std::path::PathBuf; - use std::rc::Rc; - use crate::model::xmltv::{TVGuide}; #[test] @@ -181,7 +179,7 @@ mod tests { let tv_guide = TVGuide { file: PathBuf::from(file_path) }; let channel_ids = vec!["channel.1", "channel.2", "channel.3"]; - let channel_ids : HashSet> = channel_ids.into_iter().map(|s| Arc::new(s.to_string())).collect(); + let channel_ids : HashSet = channel_ids.into_iter().map(|s| s.to_string()).collect(); match tv_guide.filter(&channel_ids) { None => assert!(false, "No epg filtered"), diff --git a/src/repository/playlist_repository.rs b/src/repository/playlist_repository.rs index ef6031d6f..900e7d23b 100644 --- a/src/repository/playlist_repository.rs +++ b/src/repository/playlist_repository.rs @@ -11,8 +11,7 @@ use crate::repository::storage::{ensure_target_storage_path, get_target_id_mappi use crate::repository::target_id_mapping::TargetIdMapping; use crate::repository::xtream_repository::xtream_write_playlist; use crate::utils::file::file_lock_manager::FileWriteGuard; - -pub const HLS_EXT: &str = ".m3u8"; +use crate::utils::network::request::is_hls_url; pub async fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, target: &ConfigTarget, cfg: &Config) -> Result<(), Vec> { @@ -30,7 +29,7 @@ pub async fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, let header = &mut channel.header; let provider_id = header.get_provider_id().unwrap_or_default(); if provider_id == 0 { - header.item_type = match (header.url.ends_with(HLS_EXT), header.item_type) { + header.item_type = match (is_hls_url(&header.url), header.item_type) { (true, _) => PlaylistItemType::LiveHls, (false, PlaylistItemType::Live) => PlaylistItemType::LiveUnknown, _ => header.item_type, diff --git a/src/utils/network/request.rs b/src/utils/network/request.rs index ec0803d20..7221dd5ef 100644 --- a/src/utils/network/request.rs +++ b/src/utils/network/request.rs @@ -26,6 +26,8 @@ use crate::utils::file::file_utils::{get_file_path, persist_file}; use crate::m3u_filter_error::create_m3u_filter_error_result; use crate::utils::debug_if_enabled; +pub const HLS_EXT: &str = ".m3u8"; + pub const fn bytes_to_megabytes(bytes: u64) -> u64 { bytes / 1_048_576 } @@ -426,22 +428,48 @@ pub fn classify_content_type(headers: &[(String, String)]) -> MimeCategory { }) } -pub fn replace_extension(path: &str, new_ext: &str) -> String { - let ext = if let Some(stripped) = new_ext.strip_prefix('.') { stripped } else { new_ext }; - if let Some(pos) = path.rfind('/') { - if let Some(dot_pos) = path[pos..].rfind('.') { - let dot_index = pos + dot_pos; - return format!("{}{}.{}", &path[..dot_index], "", ext); +const HLS_EXT_QUERY: &str = ".m3u8?"; +const HLS_EXT_FRAGMENT: &str = ".m3u8#"; + +pub fn is_hls_url(url: &str) -> bool { + let lc_url = url.to_lowercase(); + lc_url.ends_with(HLS_EXT) || lc_url.contains(HLS_EXT_QUERY) || lc_url.contains(HLS_EXT_FRAGMENT) +} + +pub fn replace_url_extension(url: &str, new_ext: &str) -> String { + let ext = new_ext.strip_prefix('.').unwrap_or(new_ext); // Remove leading dot if exists + + // Split URL into the base part (domain and path) and the suffix (query/fragment) + let (base_url, suffix) = match url.find(['?', '#'].as_ref()) { + Some(pos) => (&url[..pos], &url[pos..]), // Base URL and suffix + None => (url, ""), // No query or fragment + }; + + // Find the last '/' in the base URL, which marks the end of the domain and the beginning of the file path + if let Some(last_slash_pos) = base_url.rfind('/') { + if last_slash_pos < 9 { // protocol slash, return url as is + return url.to_string(); + } + let (path_part, file_name_with_extension) = base_url.split_at(last_slash_pos + 1); + // Find the last dot in the file name to replace the extension + if let Some(dot_pos) = file_name_with_extension.rfind('.') { + return format!( + "{}{}.{}{}", + path_part, + &file_name_with_extension[..dot_pos], // Keep the name part before the dot + ext, // Add the new extension + suffix // Add the query or fragment if any + ); } - } else if let Some(dot_pos) = path.rfind('.') { - return format!("{}{}.{}", &path[..dot_pos], "", ext); } - format!("{path}.{ext}") + + // If no extension is found, add the new extension to the base URL + format!("{}{}.{}{}", base_url, "", ext, suffix) } #[cfg(test)] mod tests { - use crate::utils::network::request::{replace_extension, sanitize_sensitive_info}; + use crate::utils::network::request::{replace_url_extension, sanitize_sensitive_info}; #[test] fn test_url_mask() { @@ -454,17 +482,15 @@ mod tests { #[test] fn test_replace_ext() { let tests = [ - "test.txt", - "folder/test.txt", - "folder/subfolder/file", - "/absolute/path/to/file.tar.gz", - "/home/user/script", - "no_extension", - "some/path/file.with.dots", + ("http://hello.world.com", "http://hello.world.com"), + ("http://hello.world.com/123", "http://hello.world.com/123.mp4"), + ("http://hello.world.com/123.ts?hello=world", "http://hello.world.com/123.mp4?hello=world"), + ("http://hello.world.com/123?hello=world", "http://hello.world.com/123.mp4?hello=world"), + ("http://hello.world.com/123#hello=world", "http://hello.world.com/123.mp4#hello=world") ]; - for test in &tests { - println!("{} -> {}", test, replace_extension(test, ".mp4")); + for (test, expect) in &tests { + assert_eq!(replace_url_extension(test, ".mp4"), *expect); } } }