Loading nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -1728,6 +1728,7 @@ ./services/web-apps/suwayomi-server.nix ./services/web-apps/szurubooru.nix ./services/web-apps/trilium.nix ./services/web-apps/tuliprox.nix ./services/web-apps/umami.nix ./services/web-apps/vikunja.nix ./services/web-apps/wakapi.nix Loading nixos/modules/services/web-apps/tuliprox.nix 0 → 100644 +336 −0 Original line number Diff line number Diff line { config, pkgs, lib, utils, ... }: let cfg = config.services.tuliprox; settingsFormat = pkgs.formats.yaml { }; systemSettingsYaml = settingsFormat.generate "config.yml" cfg.systemSettings; sourceSettingsYaml = settingsFormat.generate "source.yml" cfg.sourceSettings; apiProxySettingsYaml = settingsFormat.generate "api-proxy.yml" cfg.apiProxySettings; mappingSettingsYaml = settingsFormat.generate "mapping.yml" cfg.mappingSettings; in { meta.maintainers = with lib.maintainers; [ nyanloutre ]; options.services.tuliprox = { enable = lib.mkEnableOption "Tuliprox IPTV playlist processor & proxy"; package = lib.mkPackageOption pkgs "tuliprox" { }; extraArgs = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = '' Additional command-line arguments for the systemd service. Refer to the [Tuliprox documentation] for available arguments. [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#command-line-arguments ''; }; systemSettings = lib.mkOption { type = settingsFormat.type; example = { api = { host = "0.0.0.0"; port = 8901; }; }; description = '' Main config file Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#1-configyml ''; }; sourceSettings = lib.mkOption { type = settingsFormat.type; example = { templates = [ { name = "not_red_button"; value = "NOT (Title ~ \"(?i).*red button.*\")"; } { name = "not_low_resolution"; value = "NOT (Title ~ \"(?i).*\(360p|240p\).*\")"; } { name = "all_channels"; value = "Title ~ \".*\""; } { name = "final_channel_lineup"; value = "!all_channels! AND !not_red_button! AND !not_low_resolution!"; } ]; sources = [ { inputs = [ { name = "iptv-org"; type = "m3u"; url = "https://iptv-org.github.io/iptv/countries/uk.m3u"; } ]; targets = [ { name = "iptv-org"; output = [ { type = "xtream"; } { type = "m3u"; filename = "iptv.m3u"; } { type = "hdhomerun"; username = "local"; device = "hdhr1"; } ]; filter = "!final_channel_lineup!"; options = { ignore_logo = false; share_live_streams = true; }; mapping = [ "iptv-org" ]; } ]; } ]; }; description = '' Source definitions Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#2-sourceyml ''; }; apiProxySettings = lib.mkOption { type = settingsFormat.type; example = { server = [ { name = "default"; protocol = "http"; host = "192.169.1.9"; port = 8901; timezone = "Europe/Paris"; message = "Welcome to tuliprox"; } { name = "external"; protocol = "https"; host = "tuliprox.mydomain.tv"; port = 443; timezone = "Europe/Paris"; message = "Welcome to tuliprox"; } ]; user = [ { target = "xc_m3u"; credentials = [ { username = "test1"; password = "secret1"; token = "token1"; proxy = "reverse"; server = "default"; exp_date = 1672705545; max_connections = 1; status = "Active"; } ]; } ]; }; description = '' Users and proxy configuration Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#3-api-proxy-config ''; }; mappingSettings = lib.mkOption { type = settingsFormat.type; example = { mappings = { templates = [ { name = "bbc"; value = "Title ~ \"^BBC\""; } { name = "documentary"; value = "(Group ~ \"(Documentary|Outdoor)\")"; } { name = "entertainment"; value = "Group ~ \"Entertainment\""; } { name = "pluto_tv"; value = "(Caption ~ \"Pluto TV\") AND NOT(Caption ~ \"Sports\")"; } { name = "business"; value = "Group ~ \"Business\""; } ]; mapping = [ { id = "iptv-org"; match_as_ascii = true; mapper = [ { filter = "!bbc!"; script = '' @Group = "BBC" ''; } { filter = "!documentary!"; script = '' @Group = "Documentary" ''; } { filter = "!entertainment!"; script = '' @Group = "Entertainment" ''; } { filter = "!pluto_tv!"; script = '' @Group = "Pluto TV" ''; } { filter = "!business!"; script = '' @Group = "News" ''; } { filter = "Input ~ \"iptv-org\""; script = '' @Caption = concat(@Caption, " (iptv-org)") ''; } ]; } ]; }; }; description = '' Templates configuration Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#2-mappingyml ''; }; }; config = lib.mkIf cfg.enable { services.tuliprox.systemSettings = { api = { host = lib.mkDefault "127.0.0.1"; port = lib.mkDefault 8901; web_root = lib.mkDefault "${cfg.package}/web"; }; working_dir = lib.mkDefault "\${env:STATE_DIRECTORY}/data"; backup_dir = lib.mkDefault "\${env:STATE_DIRECTORY}/backup"; custom_stream_response_path = lib.mkDefault "${cfg.package}/resources"; }; services.tuliprox.sourceSettings.sources = lib.mkDefault [ ]; services.tuliprox.apiProxySettings = { server = lib.mkDefault [ { name = "default"; protocol = "http"; host = cfg.systemSettings.api.host; port = cfg.systemSettings.api.port; timezone = if config.time.timeZone != null then config.time.timeZone else "Etc/UTC"; message = "Welcome to tuliprox"; } ]; user = lib.mkDefault [ ]; }; services.tuliprox.mappingSettings.mappings.mapping = lib.mkDefault [ ]; systemd.services.tuliprox = { description = "Tuliprox server"; serviceConfig = { ExecStart = utils.escapeSystemdExecArgs ( [ (lib.getExe cfg.package) "--server" "--config" systemSettingsYaml "--source" sourceSettingsYaml "--api-proxy" apiProxySettingsYaml "--mapping" mappingSettingsYaml ] ++ cfg.extraArgs ); Restart = "always"; DynamicUser = true; StateDirectory = "tuliprox"; CapabilityBoundingSet = ""; LockPersonality = true; MemoryDenyWriteExecute = true; PrivateDevices = true; PrivateUsers = true; ProcSubset = "pid"; ProtectClock = true; ProtectControlGroups = true; ProtectHome = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; RestrictNamespaces = true; RestrictRealtime = true; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; UMask = "0066"; }; wantedBy = [ "multi-user.target" ]; }; }; } nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -1582,6 +1582,7 @@ in trilium-server = runTestOn [ "x86_64-linux" ] ./trilium-server.nix; tsm-client-gui = runTest ./tsm-client-gui.nix; ttyd = runTest ./web-servers/ttyd.nix; tuliprox = runTest ./tuliprox.nix; tuned = runTest ./tuned.nix; tuptime = runTest ./tuptime.nix; turbovnc-headless-server = runTest ./turbovnc-headless-server.nix; Loading nixos/tests/tuliprox.nix 0 → 100644 +19 −0 Original line number Diff line number Diff line { lib, pkgs, ... }: { name = "tuliprox"; meta.maintainers = with lib.maintainers; [ nyanloutre ]; nodes.machine = { pkgs, ... }: { services.tuliprox = { enable = true; }; }; testScript = '' machine.wait_for_unit("multi-user.target") machine.wait_for_open_port(8901) machine.succeed("curl -v -s http://127.0.0.1:8901/") ''; } pkgs/by-name/tu/tuliprox/package.nix 0 → 100644 +84 −0 Original line number Diff line number Diff line { lib, fetchFromGitHub, rustPlatform, pkg-config, openssl, ffmpeg, which, rustc, wasm-bindgen-cli_0_2_104, trunk, binaryen, dart-sass, nixosTests, nix-update-script, }: rustPlatform.buildRustPackage rec { pname = "tuliprox"; version = "3.2.0"; src = fetchFromGitHub { owner = "euzu"; repo = "tuliprox"; tag = "v${version}"; hash = "sha256-G+bVKBAxviyJShq2BG4vjMiTzHhoYaiP6FXrSWeTvkU="; }; nativeBuildInputs = [ pkg-config ffmpeg which wasm-bindgen-cli_0_2_104 trunk rustc.llvmPackages.lld binaryen dart-sass ]; # Needed to get openssl-sys to use pkg-config. env = { OPENSSL_NO_VENDOR = 1; OPENSSL_LIB_DIR = "${lib.getLib openssl}/lib"; OPENSSL_DIR = "${lib.getDev openssl}"; }; cargoHash = "sha256-bDQ4pDDTINTgotTen1/SxOZBmkUmbmmwmR4/nSoSf/A="; cargoBuildFlags = "--package tuliprox"; postBuild = '' patchShebangs ./bin/build_resources.sh ./bin/build_resources.sh pushd frontend trunk build --offline --frozen --release popd ''; checkFlags = [ "--skip=processing::parser::xmltv::tests::normalize" "--skip=processing::parser::xtream::tests::test_read_json_file_into_struct" "--skip=repository::indexed_document::tests::test_read_xt" ]; postInstall = '' cp -rf frontend/dist $out/web mkdir -p $out/resources cp -rf resources/*.ts $out/resources ''; passthru = { tests = { inherit (nixosTests) tuliprox; }; updateScript = nix-update-script { }; }; meta = { description = "Flexible IPTV playlist processor & proxy in Rust"; homepage = "https://github.com/euzu/tuliprox"; changelog = "https://github.com/euzu/tuliprox/blob/${src.tag}/CHANGELOG.md"; mainProgram = "tuliprox"; license = with lib.licenses; [ mit ]; maintainers = with lib.maintainers; [ nyanloutre ]; }; } Loading
nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -1728,6 +1728,7 @@ ./services/web-apps/suwayomi-server.nix ./services/web-apps/szurubooru.nix ./services/web-apps/trilium.nix ./services/web-apps/tuliprox.nix ./services/web-apps/umami.nix ./services/web-apps/vikunja.nix ./services/web-apps/wakapi.nix Loading
nixos/modules/services/web-apps/tuliprox.nix 0 → 100644 +336 −0 Original line number Diff line number Diff line { config, pkgs, lib, utils, ... }: let cfg = config.services.tuliprox; settingsFormat = pkgs.formats.yaml { }; systemSettingsYaml = settingsFormat.generate "config.yml" cfg.systemSettings; sourceSettingsYaml = settingsFormat.generate "source.yml" cfg.sourceSettings; apiProxySettingsYaml = settingsFormat.generate "api-proxy.yml" cfg.apiProxySettings; mappingSettingsYaml = settingsFormat.generate "mapping.yml" cfg.mappingSettings; in { meta.maintainers = with lib.maintainers; [ nyanloutre ]; options.services.tuliprox = { enable = lib.mkEnableOption "Tuliprox IPTV playlist processor & proxy"; package = lib.mkPackageOption pkgs "tuliprox" { }; extraArgs = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = '' Additional command-line arguments for the systemd service. Refer to the [Tuliprox documentation] for available arguments. [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#command-line-arguments ''; }; systemSettings = lib.mkOption { type = settingsFormat.type; example = { api = { host = "0.0.0.0"; port = 8901; }; }; description = '' Main config file Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#1-configyml ''; }; sourceSettings = lib.mkOption { type = settingsFormat.type; example = { templates = [ { name = "not_red_button"; value = "NOT (Title ~ \"(?i).*red button.*\")"; } { name = "not_low_resolution"; value = "NOT (Title ~ \"(?i).*\(360p|240p\).*\")"; } { name = "all_channels"; value = "Title ~ \".*\""; } { name = "final_channel_lineup"; value = "!all_channels! AND !not_red_button! AND !not_low_resolution!"; } ]; sources = [ { inputs = [ { name = "iptv-org"; type = "m3u"; url = "https://iptv-org.github.io/iptv/countries/uk.m3u"; } ]; targets = [ { name = "iptv-org"; output = [ { type = "xtream"; } { type = "m3u"; filename = "iptv.m3u"; } { type = "hdhomerun"; username = "local"; device = "hdhr1"; } ]; filter = "!final_channel_lineup!"; options = { ignore_logo = false; share_live_streams = true; }; mapping = [ "iptv-org" ]; } ]; } ]; }; description = '' Source definitions Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#2-sourceyml ''; }; apiProxySettings = lib.mkOption { type = settingsFormat.type; example = { server = [ { name = "default"; protocol = "http"; host = "192.169.1.9"; port = 8901; timezone = "Europe/Paris"; message = "Welcome to tuliprox"; } { name = "external"; protocol = "https"; host = "tuliprox.mydomain.tv"; port = 443; timezone = "Europe/Paris"; message = "Welcome to tuliprox"; } ]; user = [ { target = "xc_m3u"; credentials = [ { username = "test1"; password = "secret1"; token = "token1"; proxy = "reverse"; server = "default"; exp_date = 1672705545; max_connections = 1; status = "Active"; } ]; } ]; }; description = '' Users and proxy configuration Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#3-api-proxy-config ''; }; mappingSettings = lib.mkOption { type = settingsFormat.type; example = { mappings = { templates = [ { name = "bbc"; value = "Title ~ \"^BBC\""; } { name = "documentary"; value = "(Group ~ \"(Documentary|Outdoor)\")"; } { name = "entertainment"; value = "Group ~ \"Entertainment\""; } { name = "pluto_tv"; value = "(Caption ~ \"Pluto TV\") AND NOT(Caption ~ \"Sports\")"; } { name = "business"; value = "Group ~ \"Business\""; } ]; mapping = [ { id = "iptv-org"; match_as_ascii = true; mapper = [ { filter = "!bbc!"; script = '' @Group = "BBC" ''; } { filter = "!documentary!"; script = '' @Group = "Documentary" ''; } { filter = "!entertainment!"; script = '' @Group = "Entertainment" ''; } { filter = "!pluto_tv!"; script = '' @Group = "Pluto TV" ''; } { filter = "!business!"; script = '' @Group = "News" ''; } { filter = "Input ~ \"iptv-org\""; script = '' @Caption = concat(@Caption, " (iptv-org)") ''; } ]; } ]; }; }; description = '' Templates configuration Refer to the [Tuliprox documentation] for available attributes [Tuliprox documentation]: https://github.com/euzu/tuliprox?tab=readme-ov-file#2-mappingyml ''; }; }; config = lib.mkIf cfg.enable { services.tuliprox.systemSettings = { api = { host = lib.mkDefault "127.0.0.1"; port = lib.mkDefault 8901; web_root = lib.mkDefault "${cfg.package}/web"; }; working_dir = lib.mkDefault "\${env:STATE_DIRECTORY}/data"; backup_dir = lib.mkDefault "\${env:STATE_DIRECTORY}/backup"; custom_stream_response_path = lib.mkDefault "${cfg.package}/resources"; }; services.tuliprox.sourceSettings.sources = lib.mkDefault [ ]; services.tuliprox.apiProxySettings = { server = lib.mkDefault [ { name = "default"; protocol = "http"; host = cfg.systemSettings.api.host; port = cfg.systemSettings.api.port; timezone = if config.time.timeZone != null then config.time.timeZone else "Etc/UTC"; message = "Welcome to tuliprox"; } ]; user = lib.mkDefault [ ]; }; services.tuliprox.mappingSettings.mappings.mapping = lib.mkDefault [ ]; systemd.services.tuliprox = { description = "Tuliprox server"; serviceConfig = { ExecStart = utils.escapeSystemdExecArgs ( [ (lib.getExe cfg.package) "--server" "--config" systemSettingsYaml "--source" sourceSettingsYaml "--api-proxy" apiProxySettingsYaml "--mapping" mappingSettingsYaml ] ++ cfg.extraArgs ); Restart = "always"; DynamicUser = true; StateDirectory = "tuliprox"; CapabilityBoundingSet = ""; LockPersonality = true; MemoryDenyWriteExecute = true; PrivateDevices = true; PrivateUsers = true; ProcSubset = "pid"; ProtectClock = true; ProtectControlGroups = true; ProtectHome = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; RestrictNamespaces = true; RestrictRealtime = true; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; UMask = "0066"; }; wantedBy = [ "multi-user.target" ]; }; }; }
nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -1582,6 +1582,7 @@ in trilium-server = runTestOn [ "x86_64-linux" ] ./trilium-server.nix; tsm-client-gui = runTest ./tsm-client-gui.nix; ttyd = runTest ./web-servers/ttyd.nix; tuliprox = runTest ./tuliprox.nix; tuned = runTest ./tuned.nix; tuptime = runTest ./tuptime.nix; turbovnc-headless-server = runTest ./turbovnc-headless-server.nix; Loading
nixos/tests/tuliprox.nix 0 → 100644 +19 −0 Original line number Diff line number Diff line { lib, pkgs, ... }: { name = "tuliprox"; meta.maintainers = with lib.maintainers; [ nyanloutre ]; nodes.machine = { pkgs, ... }: { services.tuliprox = { enable = true; }; }; testScript = '' machine.wait_for_unit("multi-user.target") machine.wait_for_open_port(8901) machine.succeed("curl -v -s http://127.0.0.1:8901/") ''; }
pkgs/by-name/tu/tuliprox/package.nix 0 → 100644 +84 −0 Original line number Diff line number Diff line { lib, fetchFromGitHub, rustPlatform, pkg-config, openssl, ffmpeg, which, rustc, wasm-bindgen-cli_0_2_104, trunk, binaryen, dart-sass, nixosTests, nix-update-script, }: rustPlatform.buildRustPackage rec { pname = "tuliprox"; version = "3.2.0"; src = fetchFromGitHub { owner = "euzu"; repo = "tuliprox"; tag = "v${version}"; hash = "sha256-G+bVKBAxviyJShq2BG4vjMiTzHhoYaiP6FXrSWeTvkU="; }; nativeBuildInputs = [ pkg-config ffmpeg which wasm-bindgen-cli_0_2_104 trunk rustc.llvmPackages.lld binaryen dart-sass ]; # Needed to get openssl-sys to use pkg-config. env = { OPENSSL_NO_VENDOR = 1; OPENSSL_LIB_DIR = "${lib.getLib openssl}/lib"; OPENSSL_DIR = "${lib.getDev openssl}"; }; cargoHash = "sha256-bDQ4pDDTINTgotTen1/SxOZBmkUmbmmwmR4/nSoSf/A="; cargoBuildFlags = "--package tuliprox"; postBuild = '' patchShebangs ./bin/build_resources.sh ./bin/build_resources.sh pushd frontend trunk build --offline --frozen --release popd ''; checkFlags = [ "--skip=processing::parser::xmltv::tests::normalize" "--skip=processing::parser::xtream::tests::test_read_json_file_into_struct" "--skip=repository::indexed_document::tests::test_read_xt" ]; postInstall = '' cp -rf frontend/dist $out/web mkdir -p $out/resources cp -rf resources/*.ts $out/resources ''; passthru = { tests = { inherit (nixosTests) tuliprox; }; updateScript = nix-update-script { }; }; meta = { description = "Flexible IPTV playlist processor & proxy in Rust"; homepage = "https://github.com/euzu/tuliprox"; changelog = "https://github.com/euzu/tuliprox/blob/${src.tag}/CHANGELOG.md"; mainProgram = "tuliprox"; license = with lib.licenses; [ mit ]; maintainers = with lib.maintainers; [ nyanloutre ]; }; }