{ inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; outputs = { nixpkgs, ... }: let lib = nixpkgs.lib; systems = [ "x86_64-linux" "aarch64-linux" ]; pkgsFor = system: nixpkgs.legacyPackages.${system}; appFor = pkgs: let src = lib.fileset.toSource { root = ./.; fileset = lib.fileset.unions [ ./rest ./mod ./providers ./package.json ./package-lock.json ]; }; npmDeps = pkgs.fetchNpmDeps { name = "streamreverse-npm-deps"; inherit src; hash = "sha256-pMxVG+OHiqjz1GHWDhhNOsrhoVPzVJ5kXhiRsP0Rflg="; }; in pkgs.stdenvNoCC.mkDerivation { pname = "streamreverse"; version = "0.1.0"; inherit src npmDeps; nativeBuildInputs = [ pkgs.nodejs pkgs.npmHooks.npmConfigHook ]; dontConfigure = true; dontBuild = true; installPhase = '' mkdir -p $out/lib/bug-tv $out/bin cp -r rest mod providers package.json node_modules $out/lib/bug-tv/ printf '%s\n' \ '#!${pkgs.bash}/bin/bash' \ 'exec ${pkgs.nodejs}/bin/node "$out/lib/bug-tv/rest/server.js" "$@"' \ > $out/bin/bug-tv chmod 755 $out/bin/bug-tv ''; }; in { packages = lib.genAttrs systems (system: { default = appFor (pkgsFor system); }); devShells = lib.genAttrs systems (system: { default = let pkgs = pkgsFor system; in pkgs.mkShell { packages = [ pkgs.nodejs pkgs.nodejs-npm ]; }; }); nixosModules.default = { config, lib, pkgs, ... }: let cfg = config.services."bug-tv"; baseFilters = { minQuality = cfg.filters.minQuality; audioCodec = cfg.filters.audioCodec; subtitles = cfg.filters.subtitles; subtitleLang = cfg.filters.subtitleLang; }; baseConfig = { tmdbKey = cfg.tmdbApiKey; enabledProviders = cfg.providers; timeoutMs = cfg.timeoutMs; apiHost = cfg.host; apiPort = cfg.port; filters = baseFilters; }; mergedConfig = (baseConfig // (lib.removeAttrs cfg.extraConfig [ "filters" ])) // lib.optionalAttrs (cfg.extraConfig ? "filters") { filters = baseFilters // cfg.extraConfig.filters; }; configJson = pkgs.writeText "bug-tv-config.json" (builtins.toJSON mergedConfig); app = pkgs.runCommand "bug-tv" { preferLocalBuild = true; } '' mkdir -p $out for path in rest mod providers package.json node_modules; do cp -a ${cfg.package}/lib/bug-tv/''${path} $out/ done cp ${configJson} $out/config.json mkdir -p $out/bin printf '%s\n' \ '#!${pkgs.bash}/bin/bash' \ 'exec ${pkgs.nodejs}/bin/node "$out/rest/server.js" "$@"' \ > $out/bin/bug-tv chmod 755 $out/bin/bug-tv ''; in { options.services."bug-tv" = lib.mkOption { default = { }; type = lib.types.submodule { options = { enable = lib.mkEnableOption "the bug.tv multi-provider stream finder API"; package = lib.mkOption { description = "The streamreverse package to run."; type = lib.types.package; }; port = lib.mkOption { description = "TCP port to listen on."; type = lib.types.port; default = 8789; }; host = lib.mkOption { description = "Interface to bind."; type = lib.types.str; default = "0.0.0.0"; }; tmdbApiKey = lib.mkOption { description = "TMDB API key used for search and details lookups."; type = lib.types.str; default = ""; }; providers = lib.mkOption { description = "Providers enabled by default when none are requested."; type = lib.types.listOf lib.types.str; default = [ "cineby" ]; }; timeoutMs = lib.mkOption { description = "Per-provider scrape timeout in milliseconds."; type = lib.types.int; default = 30000; }; filters = lib.mkOption { description = "Default stream filters."; default = { }; type = lib.types.submodule { options = { minQuality = lib.mkOption { description = "Minimum quality (e.g. 1080p, or any)."; type = lib.types.str; default = "any"; }; audioCodec = lib.mkOption { description = "Required audio codec, or any."; type = lib.types.str; default = "any"; }; subtitles = lib.mkOption { description = "Subtitle requirement (any/required/none)."; type = lib.types.str; default = "any"; }; subtitleLang = lib.mkOption { description = "Required subtitle language, empty for any."; type = lib.types.str; default = ""; }; }; }; }; extraConfig = lib.mkOption { description = "Extra keys merged into the generated config.json."; type = lib.types.attrsOf lib.types.json; default = { }; }; }; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = cfg.tmdbApiKey != ""; message = "services.bug-tv.tmdbApiKey must be set"; } { assertion = cfg.providers != [ ]; message = "services.bug-tv.providers must not be empty"; } ]; users.users."bug-tv" = { isSystemUser = true; group = "bug-tv"; }; users.groups."bug-tv" = { }; systemd.services."bug-tv" = { description = "bug.tv stream finder API"; wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; serviceConfig = { User = "bug-tv"; Group = "bug-tv"; ExecStart = "${app}/bin/bug-tv"; Restart = "always"; RestartSec = 5; NoNewPrivileges = true; PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = true; LockPersonality = true; }; }; }; }; }; }