jellyfin: update jellyfin cli for 10.11.6
This commit is contained in:
parent
6d4a0eb437
commit
abd3bf7ceb
3 changed files with 77 additions and 45 deletions
|
|
@ -13,31 +13,38 @@ let
|
|||
|
||||
fqdn = "${cfg.subdomain}.${cfg.domain}";
|
||||
|
||||
jellyfin-cli = pkgs.buildDotnetModule rec {
|
||||
pname = "jellyfin-cli";
|
||||
version = "10.10.7";
|
||||
jellyfin = pkgs.buildDotnetModule rec {
|
||||
pname = "jellyfin";
|
||||
version = "10.11.6";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "ibizaman";
|
||||
repo = "jellyfin";
|
||||
rev = "0b1a5d929960f852dba90c1fc36f3a19dc094f8d";
|
||||
hash = "sha256-H9V65+886EYMn/xDEgmxvoEOrbZaI1wSfmkN9vAzGhw=";
|
||||
rev = "c58ca41d9ee76d137be788cd6f2d089e288ad561";
|
||||
hash = "sha256-gTHsz5qRT+9FjAqBb4hDBkHChYDU52snBWu6cQb10i4=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [ pkgs.sqlite ];
|
||||
|
||||
projectFile = "Jellyfin.Cli/Jellyfin.Cli.csproj";
|
||||
executables = [ "jellyfin-cli" ];
|
||||
projectFile = "Jellyfin.Server/Jellyfin.Server.csproj";
|
||||
executables = [ "jellyfin" ];
|
||||
nugetDeps = "${pkgs.path}/pkgs/by-name/je/jellyfin/nuget-deps.json";
|
||||
runtimeDeps = [
|
||||
pkgs.jellyfin-ffmpeg
|
||||
pkgs.fontconfig
|
||||
pkgs.freetype
|
||||
];
|
||||
dotnet-sdk = pkgs.dotnetCorePackages.sdk_8_0;
|
||||
dotnet-runtime = pkgs.dotnetCorePackages.aspnetcore_8_0;
|
||||
dotnet-sdk = pkgs.dotnetCorePackages.sdk_9_0;
|
||||
dotnet-runtime = pkgs.dotnetCorePackages.aspnetcore_9_0;
|
||||
dotnetBuildFlags = [ "--no-self-contained" ];
|
||||
|
||||
makeWrapperArgs = [
|
||||
"--append-flags"
|
||||
"--ffmpeg=${pkgs.jellyfin-ffmpeg}/bin/ffmpeg"
|
||||
"--append-flags"
|
||||
"--webdir=${pkgs.jellyfin-web}/share/jellyfin-web"
|
||||
];
|
||||
|
||||
passthru.tests = {
|
||||
smoke-test = pkgs.nixosTests.jellyfin;
|
||||
};
|
||||
|
|
@ -53,7 +60,7 @@ let
|
|||
purcell
|
||||
jojosch
|
||||
];
|
||||
mainProgram = "jellyfin-cli";
|
||||
mainProgram = "jellyfin";
|
||||
platforms = dotnet-runtime.meta.platforms;
|
||||
};
|
||||
};
|
||||
|
|
@ -169,9 +176,9 @@ in
|
|||
description = "Pluging used for LDAP authentication.";
|
||||
default = shb.mkJellyfinPlugin (rec {
|
||||
pname = "jellyfin-plugin-ldapauth";
|
||||
version = "20";
|
||||
version = "22";
|
||||
url = "https://github.com/jellyfin/${pname}/releases/download/v${version}/ldap-authentication_${version}.0.0.0.zip";
|
||||
hash = "sha256-qATHNuiC6u+/vbY610jrFZSC16FG+Zpdjo+dfOVdVIk=";
|
||||
hash = "sha256-m2oD9woEuoSRiV9OeifAxZN7XQULMKS0Yq4TF+LjjpI=";
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -232,9 +239,9 @@ in
|
|||
description = "Pluging used for SSO authentication.";
|
||||
default = shb.mkJellyfinPlugin (rec {
|
||||
pname = "jellyfin-plugin-sso";
|
||||
version = "3.5.2.4";
|
||||
version = "4.0.0.3";
|
||||
url = "https://github.com/9p4/${pname}/releases/download/v${version}/sso-authentication_${version}.zip";
|
||||
hash = "sha256-e+w5m6/7vRAynStDj34eBexfCIEgDJ09huHzi5gQEbo=";
|
||||
hash = "sha256-Jkuc+Ua7934iSutf/zTY1phTxaltUkfiujOkCi7BW8w=";
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -340,6 +347,7 @@ in
|
|||
];
|
||||
|
||||
services.jellyfin.enable = true;
|
||||
services.jellyfin.package = jellyfin;
|
||||
|
||||
networking.firewall = {
|
||||
# from https://jellyfin.org/docs/general/networking/index.html, for auto-discovery
|
||||
|
|
@ -730,6 +738,7 @@ in
|
|||
let
|
||||
# We must always wait for the service to be fully initialized,
|
||||
# even if we're planning on changing the config and restarting.
|
||||
# And the service is not initialized until this URL returns a 200 and not a 503.
|
||||
waitForCurl = pkgs.writeShellApplication {
|
||||
name = "waitForCurl";
|
||||
runtimeInputs = [ pkgs.curl ];
|
||||
|
|
@ -770,14 +779,14 @@ in
|
|||
#
|
||||
# If the file does not exist, write the config, create the file then restart.
|
||||
# If the file exists, do nothing and remove the file, resetting the state for the next time.
|
||||
restartedFile = "${config.services.jellyfin.dataDir}/.jellyfin-restarted";
|
||||
restartedFile = "${config.services.jellyfin.dataDir}/shb-jellyfin-restarted";
|
||||
|
||||
writeConfig = pkgs.writeShellApplication {
|
||||
name = "writeConfig";
|
||||
runtimeInputs = [ pkgs.systemd ];
|
||||
text = ''
|
||||
if ! [ -f "${restartedFile}" ]; then
|
||||
${lib.getExe jellyfin-cli} wizard \
|
||||
${lib.getExe config.services.jellyfin.package} config \
|
||||
--datadir='${config.services.jellyfin.dataDir}' \
|
||||
--configdir='${config.services.jellyfin.configDir}' \
|
||||
--cachedir='${config.services.jellyfin.cacheDir}' \
|
||||
|
|
@ -799,7 +808,7 @@ in
|
|||
rm "${restartedFile}"
|
||||
else
|
||||
echo "Restarting jellyfin.service"
|
||||
touch "${restartedFile}"
|
||||
echo "This file is used by SelfHostBlocks to know when to restart jellyfin" > "${restartedFile}"
|
||||
systemctl reload-or-restart jellyfin.service
|
||||
fi
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -99,19 +99,23 @@ let
|
|||
# otherwise curl will not be able to verify the "legitimacy of the server".
|
||||
+ lib.strings.concatMapStrings (
|
||||
u:
|
||||
let
|
||||
url = if builtins.isString u then u else u.url;
|
||||
status = if builtins.isString u then 200 else u.status;
|
||||
in
|
||||
''
|
||||
import time
|
||||
|
||||
done = False
|
||||
count = 15
|
||||
while not done and count > 0:
|
||||
response = curl(client, """{"code":%{response_code}}""", "${u}")
|
||||
response = curl(client, """{"code":%{response_code}}""", "${url}")
|
||||
time.sleep(5)
|
||||
count -= 1
|
||||
if isinstance(response, dict):
|
||||
done = response.get('code') == 200
|
||||
done = response.get('code') == ${toString status}
|
||||
if not done:
|
||||
raise Exception(f"Response was never 200, got last: {response}")
|
||||
raise Exception(f"Response was never ${toString status}, got last: {response}")
|
||||
''
|
||||
+ "\n"
|
||||
) (waitForUrls args)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
let
|
||||
port = 9096;
|
||||
|
||||
adminUser = "jellyfin2";
|
||||
adminPassword = "admin";
|
||||
|
||||
commonTestScript = shb.test.mkScripts {
|
||||
hasSSL = { node, ... }: !(isNull node.config.shb.jellyfin.ssl);
|
||||
waitForServices =
|
||||
|
|
@ -19,24 +22,36 @@ let
|
|||
{ proto_fqdn, ... }:
|
||||
[
|
||||
"${proto_fqdn}/System/Info/Public"
|
||||
{
|
||||
url = "${proto_fqdn}/Users/AuthenticateByName";
|
||||
status = 401;
|
||||
}
|
||||
];
|
||||
extraScript =
|
||||
{ node, ... }:
|
||||
''
|
||||
server.wait_until_succeeds("journalctl --since -1m --unit jellyfin --grep 'Startup complete'")
|
||||
headers = unline_with(" ", """
|
||||
-H 'Content-Type: application/json'
|
||||
-H 'Authorization: MediaBrowser Client="Android TV", Device="Nvidia Shield", DeviceId="ZQ9YQHHrUzk24vV", Version="0.15.3"'
|
||||
""")
|
||||
import time
|
||||
with subtest("api login success"):
|
||||
response = curl(client, """{"code":%{response_code}}""", "${node.config.test.proto_fqdn}/Users/AuthenticateByName",
|
||||
data="""{"Username": "jellyfin", "Pw": "admin"}""",
|
||||
extra=headers)
|
||||
if response['code'] != 200:
|
||||
ok = False
|
||||
for i in range(1, 5):
|
||||
response = curl(client, """{"code":%{response_code}}""", "${node.config.test.proto_fqdn}/Users/AuthenticateByName",
|
||||
data="""{"Username": "${adminUser}", "Pw": "${adminPassword}"}""",
|
||||
extra=headers)
|
||||
if response['code'] == 200:
|
||||
ok = True
|
||||
break
|
||||
time.sleep(5)
|
||||
if not ok:
|
||||
raise Exception(f"Expected success, got: {response['code']}")
|
||||
|
||||
with subtest("api login failure"):
|
||||
response = curl(client, """{"code":%{response_code}}""", "${node.config.test.proto_fqdn}/Users/AuthenticateByName",
|
||||
data="""{"Username": "jellyfin", "Pw": "badpassword"}""",
|
||||
data="""{"Username": "${adminUser}", "Pw": "badpassword"}""",
|
||||
extra=headers)
|
||||
if response['code'] != 401:
|
||||
raise Exception(f"Expected failure, got: {response['code']}")
|
||||
|
|
@ -50,6 +65,9 @@ let
|
|||
shb.test.baseModule
|
||||
../../modules/services/jellyfin.nix
|
||||
];
|
||||
# Jellyfin checks for minimum 2Gib on startup.
|
||||
virtualisation.diskSize = 4096;
|
||||
virtualisation.memorySize = 4096;
|
||||
test = {
|
||||
subdomain = "j";
|
||||
};
|
||||
|
|
@ -59,7 +77,7 @@ let
|
|||
inherit (config.test) subdomain domain;
|
||||
inherit port;
|
||||
admin = {
|
||||
username = "jellyfin";
|
||||
username = adminUser;
|
||||
password.result = config.shb.hardcodedsecret.jellyfinAdminPassword.result;
|
||||
};
|
||||
debug = true;
|
||||
|
|
@ -67,7 +85,7 @@ let
|
|||
|
||||
shb.hardcodedsecret.jellyfinAdminPassword = {
|
||||
request = config.shb.jellyfin.admin.password.request;
|
||||
settings.content = "admin";
|
||||
settings.content = adminPassword;
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
|
|
@ -95,18 +113,18 @@ let
|
|||
loginButtonNameRegex = "Sign In";
|
||||
testLoginWith = [
|
||||
{
|
||||
username = "jellyfin";
|
||||
username = adminUser;
|
||||
password = "badpassword";
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).to_be_visible(timeout=10000)"
|
||||
];
|
||||
}
|
||||
{
|
||||
username = "jellyfin";
|
||||
password = "admin";
|
||||
username = adminUser;
|
||||
password = adminPassword;
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Uu]ser'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Pp]assword$'))).not_to_be_visible(timeout=10000)"
|
||||
|
|
@ -181,18 +199,18 @@ let
|
|||
loginButtonNameRegex = "Sign In";
|
||||
testLoginWith = [
|
||||
{
|
||||
username = "jellyfin";
|
||||
username = adminUser;
|
||||
password = "badpassword";
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).to_be_visible(timeout=10000)"
|
||||
];
|
||||
}
|
||||
{
|
||||
username = "jellyfin";
|
||||
password = "admin";
|
||||
username = adminUser;
|
||||
password = adminPassword;
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Uu]ser'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Pp]assword$'))).not_to_be_visible(timeout=10000)"
|
||||
|
|
@ -202,7 +220,7 @@ let
|
|||
username = "alice";
|
||||
password = "AlicePassword";
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# For a reason I can't explain, redirection needs to happen manually.
|
||||
"page.goto('${config.test.proto}://${config.test.fqdn}/web/')"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).not_to_be_visible(timeout=10000)"
|
||||
|
|
@ -214,7 +232,7 @@ let
|
|||
username = "alice";
|
||||
password = "NotAlicePassword";
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).to_be_visible(timeout=10000)"
|
||||
];
|
||||
}
|
||||
|
|
@ -222,7 +240,7 @@ let
|
|||
username = "bob";
|
||||
password = "BobPassword";
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# For a reason I can't explain, redirection needs to happen manually.
|
||||
"page.goto('${config.test.proto}://${config.test.fqdn}/web/')"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).not_to_be_visible(timeout=10000)"
|
||||
|
|
@ -234,7 +252,7 @@ let
|
|||
username = "bob";
|
||||
password = "NotBobPassword";
|
||||
nextPageExpect = [
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).to_be_visible(timeout=10000)"
|
||||
];
|
||||
}
|
||||
|
|
@ -300,7 +318,7 @@ let
|
|||
"page.get_by_text(re.compile('[Aa]ccept')).click()"
|
||||
# For a reason I can't explain, redirection needs to happen manually.
|
||||
"page.goto('${config.test.proto}://${config.test.fqdn}/web/')"
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Uu]ser'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Pp]assword$'))).not_to_be_visible(timeout=10000)"
|
||||
|
|
@ -313,7 +331,7 @@ let
|
|||
# For a reason I can't explain, redirection needs to happen manually.
|
||||
# So for failing auth, we check we're back on the login page.
|
||||
"page.goto('${config.test.proto}://${config.test.fqdn}/web/')"
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_label(re.compile('^[Uu]ser'))).to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Pp]assword$'))).to_be_visible(timeout=10000)"
|
||||
];
|
||||
|
|
@ -325,7 +343,7 @@ let
|
|||
"page.get_by_text(re.compile('[Aa]ccept')).click()"
|
||||
# For a reason I can't explain, redirection needs to happen manually.
|
||||
"page.goto('${config.test.proto}://${config.test.fqdn}/web/')"
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_text(re.compile('[Ii]nvalid'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Uu]ser'))).not_to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Pp]assword$'))).not_to_be_visible(timeout=10000)"
|
||||
|
|
@ -337,7 +355,7 @@ let
|
|||
nextPageExpect = [
|
||||
# For a reason I can't explain, redirection needs to happen manually.
|
||||
"page.goto('${config.test.proto}://${config.test.fqdn}/web/')"
|
||||
"expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
# "expect(page).to_have_title(re.compile('Jellyfin'))"
|
||||
"expect(page.get_by_label(re.compile('^[Uu]ser'))).to_be_visible(timeout=10000)"
|
||||
"expect(page.get_by_label(re.compile('^[Pp]assword$'))).to_be_visible(timeout=10000)"
|
||||
];
|
||||
|
|
@ -352,6 +370,7 @@ let
|
|||
shb.test.runNixOSTest {
|
||||
name = "jellyfin_${name}";
|
||||
|
||||
interactive.sshBackdoor.enable = true;
|
||||
interactive.nodes.server = {
|
||||
environment.systemPackages = [
|
||||
pkgs.sqlite
|
||||
|
|
|
|||
Loading…
Reference in a new issue