Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

streamlink: add module #6031

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions modules/lib/maintainers.nix
Original file line number Diff line number Diff line change
Expand Up @@ -628,4 +628,10 @@
github = "ALameLlama";
githubId = 55490546;
};
folliehiyuki = {
name = "Hoang Nguyen";
email = "[email protected]";
github = "folliehiyuki";
githubId = 67634026;
};
}
10 changes: 10 additions & 0 deletions modules/misc/news.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,16 @@ in {
speed, features, or native UIs. Ghostty provides all three.
'';
}

{
time = "2025-01-04T04:16:57+00:00";
message = ''
A new module is available: 'programs.streamlink'.

Streamlink is a CLI utility which pipes video streams from various
services into a video player.
'';
}
];
};
}
1 change: 1 addition & 0 deletions modules/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ let
./programs/sqls.nix
./programs/ssh.nix
./programs/starship.nix
./programs/streamlink.nix
./programs/swaylock.nix
./programs/swayr.nix
./programs/taskwarrior.nix
Expand Down
143 changes: 143 additions & 0 deletions modules/programs/streamlink.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
{ config, lib, pkgs, ... }:

with lib;

let

cfg = config.programs.streamlink;

renderSettings = mapAttrsToList (name: value:
if (builtins.isBool value) then
if value then name else ""
else if (builtins.isList value) then
concatStringsSep "\n"
(builtins.map (item: "${name}=${builtins.toString item}") value)
else
"${name}=${builtins.toString value}");

pluginType = types.submodule {
options = {
src = mkOption {
type = with types; nullOr (either path lines);
default = null;
description = ''
Source of the custom plugin. The value should be a path to the
plugin file, or the text of the plugin code. Will be linked to
{file}`$XDG_DATA_HOME/streamlink/plugins/<name>.py` (linux) or
{file}`Library/Application Support/streamlink/plugins/<name>.py` (darwin).
'';
example = literalExpression "./custom_plugin.py";
};

settings = mkOption {
type = with types;
attrsOf (oneOf [ bool int str (listOf (either int str)) ]);
default = { };
example = literalExpression ''
{
quiet = true;
}
'';
description = ''
Configuration for the specific plugin, written to
{file}`$XDG_CONFIG_HOME/streamlink/config.<name>` (linux) or
{file}`Library/Application Support/streamlink/config.<name>` (darwin).
'';
};
};
};

in {
meta.maintainers = [ hm.maintainers.folliehiyuki ];

options.programs.streamlink = {
enable = mkEnableOption "streamlink";

package = mkPackageOption pkgs "streamlink" { };

settings = mkOption {
type = with types;
attrsOf (oneOf [ bool int str (listOf (either int str)) ]);
default = { };
example = literalExpression ''
{
player = "''${pkgs.mpv}/bin/mpv";
player-args = "--cache 2048";
player-no-close = true;
}
'';
description = ''
Global configuration options for streamlink. It will be written to
{file}`$XDG_CONFIG_HOME/streamlink/config` (linux) or
{file}`Library/Application Support/streamlink/config` (darwin).
'';
};

plugins = mkOption {
description = ''
Streamlink plugins.

If a source is set, the custom plugin will be linked to the data directory.

Additional configuration specific to the plugin, if defined, will be
written to the config directory, and override global settings.
'';
type = types.attrsOf pluginType;
default = { };
example = literalExpression ''
{
custom_plugin = {
src = ./custom_plugin.py;
settings = {
quiet = true;
};
};
}
'';
};
};

config = mkIf cfg.enable {
home.packages = [ cfg.package ];

home.file = mkIf pkgs.stdenv.hostPlatform.isDarwin {
"Library/Application Support/streamlink/config" =
mkIf (cfg.settings != { }) {
text = concatStringsSep "\n" (remove "" (renderSettings cfg.settings))
+ "\n";
};
} // (mapAttrs' (name: value:
nameValuePair "Library/Application Support/streamlink/config.${name}"
(mkIf (value.settings != { }) {
text = concatStringsSep "\n" (remove "" (renderSettings value.settings))
+ "\n";
})) cfg.plugins) // (mapAttrs' (name: value:
nameValuePair
"Library/Application Support/streamlink/plugins/${name}.py"
(mkIf (value.src != null) (if (builtins.isPath value.src) then {
source = value.src;
} else {
text = value.src;
}))) cfg.plugins);

xdg.configFile = mkIf pkgs.stdenv.hostPlatform.isLinux ({
"streamlink/config" = mkIf (cfg.settings != { }) {
text = concatStringsSep "\n" (remove "" (renderSettings cfg.settings))
+ "\n";
};
} // (mapAttrs' (name: value:
nameValuePair "streamlink/config.${name}" (mkIf (value.settings != { }) {
text = concatStringsSep "\n" (remove "" (renderSettings value.settings))
+ "\n";
})) cfg.plugins));

xdg.dataFile = mkIf pkgs.stdenv.hostPlatform.isLinux (mapAttrs'
(name: value:
nameValuePair "streamlink/plugins/${name}.py" (mkIf (value.src != null)
(if (builtins.isPath value.src) then {
source = value.src;
} else {
text = value.src;
}))) cfg.plugins);
};
}
1 change: 1 addition & 0 deletions tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ in import nmtSrc {
./modules/programs/spotify-player
./modules/programs/ssh
./modules/programs/starship
./modules/programs/streamlink
./modules/programs/taskwarrior
./modules/programs/tealdeer
./modules/programs/texlive
Expand Down
5 changes: 5 additions & 0 deletions tests/modules/programs/streamlink/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
http-header=User-Agent=Mozilla/5.0 (X11; Linux x86_64; rv:130.0) Gecko/20100101 Firefox/130.0
http-header=Accept-Language=en-US
player=mpv
player-args=--cache 2048
player-no-close
4 changes: 4 additions & 0 deletions tests/modules/programs/streamlink/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
streamlink-settings = ./streamlink-settings.nix;
streamlink-custom-plugins = ./streamlink-custom-plugins.nix;
}
9 changes: 9 additions & 0 deletions tests/modules/programs/streamlink/dummy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
$description Dummy plugin for testing
"""

from streamlink.plugin import Plugin

class DummyTV(Plugin):

__plugin__ = DummyTV
45 changes: 45 additions & 0 deletions tests/modules/programs/streamlink/streamlink-custom-plugins.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{ pkgs, ... }:

{
programs.streamlink = {
enable = true;
plugins = {
dummy.src = ./dummy.py;

dummy2.src = builtins.readFile ./dummy.py;

twitch.settings = {
player = "haruna";
quiet = true;
};
};
};

test.stubs.streamlink = { };

nmt.script = let
configDir = if pkgs.stdenv.isDarwin then
"Library/Application Support/streamlink"
else
".config/streamlink";

pluginDir = if pkgs.stdenv.isDarwin then
"Library/Application Support/streamlink/plugins"
else
".local/share/streamlink/plugins";
in ''
assertFileExists home-files/${configDir}/config.twitch
assertFileContent home-files/${configDir}/config.twitch ${
pkgs.writeText "expected" ''
player=haruna
quiet
''
}

assertFileExists home-files/${pluginDir}/dummy.py
assertFileContent home-files/${pluginDir}/dummy.py ${./dummy.py}

assertFileExists home-files/${pluginDir}/dummy2.py
assertFileContent home-files/${pluginDir}/dummy2.py ${./dummy.py}
'';
}
28 changes: 28 additions & 0 deletions tests/modules/programs/streamlink/streamlink-settings.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{ pkgs, ... }:

{
programs.streamlink = {
enable = true;
settings = {
player = "mpv";
player-args = "--cache 2048";
player-no-close = true;
http-header = [
"User-Agent=Mozilla/5.0 (X11; Linux x86_64; rv:130.0) Gecko/20100101 Firefox/130.0"
"Accept-Language=en-US"
];
};
};

test.stubs.streamlink = { };

nmt.script = let
streamlinkConfig = if pkgs.stdenv.hostPlatform.isDarwin then
"Library/Application Support/streamlink/config"
else
".config/streamlink/config";
in ''
assertFileExists home-files/${streamlinkConfig}
assertFileContent home-files/${streamlinkConfig} ${./config}
'';
}
Loading