embystream 0.0.36

Another Emby streaming application (frontend/backend separation) written in Rust.
Documentation
use hyper::Uri;
use regex::Regex;
use serde::{Deserialize, Serialize};

use super::{
    direct::types::DirectLink, disk::types::Disk,
    google_drive::GoogleDriveConfig, openlist::types::OpenList,
    webdav::WebDavConfig,
};
use crate::{
    config::types::{AntiReverseProxyConfig, PathRewriteConfig},
    util::path_rewriter::PathRewriter,
};

fn default_check_file_existence() -> bool {
    true
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Backend {
    pub listen_port: u16,
    pub base_url: String,
    pub port: String,
    #[serde(default)]
    pub path: String,
    #[serde(default = "default_check_file_existence")]
    pub check_file_existence: bool,
    #[serde(default)]
    pub problematic_clients: Vec<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BackendNode {
    pub name: String,
    #[serde(rename = "backend_type", alias = "type")]
    pub backend_type: String,
    #[serde(default)]
    pub pattern: String,
    #[serde(skip)]
    pub pattern_regex: Option<Regex>,
    #[serde(default)]
    pub base_url: String,
    #[serde(default)]
    pub port: String,
    #[serde(default)]
    pub path: String,
    #[serde(default)]
    pub priority: i32,
    #[serde(default = "default_proxy_mode")]
    pub proxy_mode: String,
    #[serde(default)]
    pub client_speed_limit_kbs: u64,
    #[serde(default)]
    pub client_burst_speed_kbs: u64,
    #[serde(default, rename = "path_rewrites", alias = "PathRewrite")]
    pub path_rewrites: Vec<PathRewriteConfig>,
    #[serde(
        default,
        rename = "anti_reverse_proxy",
        alias = "AntiReverseProxy"
    )]
    pub anti_reverse_proxy: AntiReverseProxyConfig,
    #[serde(skip)]
    pub path_rewriter_cache: Vec<PathRewriter>,
    #[serde(skip)]
    pub uuid: String,
    #[serde(rename = "disk", alias = "Disk")]
    pub disk: Option<Disk>,
    #[serde(rename = "open_list", alias = "OpenList")]
    pub open_list: Option<OpenList>,
    #[serde(rename = "direct_link", alias = "DirectLink")]
    pub direct_link: Option<DirectLink>,
    #[serde(rename = "google_drive", alias = "GoogleDrive")]
    pub google_drive: Option<GoogleDriveConfig>,
    #[serde(rename = "webdav", alias = "WebDav")]
    pub webdav: Option<WebDavConfig>,
}

macro_rules! impl_uri {
    ($t:ty) => {
        impl $t {
            pub fn uri(&self) -> Uri {
                if self.base_url.is_empty() {
                    return Uri::from_static("/");
                }

                let should_show_port = !self.port.is_empty()
                    && self.port != "443"
                    && self.port != "80";
                let clean_url = self.base_url.trim_end_matches('/');
                let clean_path =
                    self.path.trim_start_matches('/').trim_end_matches('/');

                let uri_str = if should_show_port {
                    format!("{}:{}/{}", clean_url, self.port, clean_path)
                } else if clean_path.is_empty() {
                    clean_url.to_string()
                } else {
                    format!("{}/{}", clean_url, clean_path)
                };

                uri_str
                    .parse::<Uri>()
                    .unwrap_or_else(|_| Uri::from_static("/"))
            }
        }
    };
}

impl_uri!(Backend);
impl_uri!(BackendNode);

fn default_proxy_mode() -> String {
    "redirect".to_string()
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "backend_type", content = "settings")]
pub enum BackendConfig {
    Disk(Disk),
    OpenList(OpenList),
    DirectLink(DirectLink),
    GoogleDrive(GoogleDriveConfig),
}