mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-01-31 17:23:34 +00:00
Merge pull request #311927 from mweinelt/music-assistant
music-assistant: init at 2.0.7
This commit is contained in:
commit
bf8439efae
@ -33,6 +33,8 @@
|
||||
|
||||
- [Renovate](https://github.com/renovatebot/renovate), a dependency updating tool for various git forges and language ecosystems. Available as [services.renovate](#opt-services.renovate.enable).
|
||||
|
||||
- [Music Assistant](https://music-assistant.io/), a music library manager for your offline and online music sources which can easily stream your favourite music to a wide range of supported players. Available as [services.music-assistant](#opt-services.music-assistant.enable).
|
||||
|
||||
- [wg-access-server](https://github.com/freifunkMUC/wg-access-server/), an all-in-one WireGuard VPN solution with a web ui for connecting devices. Available at [services.wg-access-server](#opt-services.wg-access-server.enable).
|
||||
|
||||
- [Envision](https://gitlab.com/gabmus/envision), a UI for building, configuring and running Monado, the open source OpenXR runtime. Available as [programs.envision](#opt-programs.envision.enable).
|
||||
|
@ -376,6 +376,7 @@
|
||||
./services/audio/mopidy.nix
|
||||
./services/audio/mpd.nix
|
||||
./services/audio/mpdscribble.nix
|
||||
./services/audio/music-assistant.nix
|
||||
./services/audio/mympd.nix
|
||||
./services/audio/navidrome.nix
|
||||
./services/audio/networkaudiod.nix
|
||||
|
113
nixos/modules/services/audio/music-assistant.nix
Normal file
113
nixos/modules/services/audio/music-assistant.nix
Normal file
@ -0,0 +1,113 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
mkIf
|
||||
mkEnableOption
|
||||
mkOption
|
||||
mkPackageOption
|
||||
types
|
||||
;
|
||||
|
||||
inherit (types)
|
||||
listOf
|
||||
enum
|
||||
str
|
||||
;
|
||||
|
||||
cfg = config.services.music-assistant;
|
||||
|
||||
finalPackage = cfg.package.override {
|
||||
inherit (cfg) providers;
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
meta.buildDocsInSandbox = false;
|
||||
|
||||
options.services.music-assistant = {
|
||||
enable = mkEnableOption "Music Assistant";
|
||||
|
||||
package = mkPackageOption pkgs "music-assistant" { };
|
||||
|
||||
extraOptions = mkOption {
|
||||
type = listOf str;
|
||||
default = [ "--config" "/var/lib/music-assistant" ];
|
||||
example = [
|
||||
"--log-level"
|
||||
"DEBUG"
|
||||
];
|
||||
description = ''
|
||||
List of extra options to pass to the music-assistant executable.
|
||||
'';
|
||||
};
|
||||
|
||||
providers = mkOption {
|
||||
type = listOf (enum cfg.package.providerNames);
|
||||
default = [];
|
||||
example = [
|
||||
"opensubsonic"
|
||||
"snapcast"
|
||||
];
|
||||
description = ''
|
||||
List of provider names for which dependencies will be installed.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.music-assistant = {
|
||||
description = "Music Assistant";
|
||||
documentation = [ "https://music-assistant.io" ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
environment = {
|
||||
HOME = "/var/lib/music-assistant";
|
||||
PYTHONPATH = finalPackage.pythonPath;
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = utils.escapeSystemdExecArgs ([
|
||||
(lib.getExe cfg.package)
|
||||
] ++ cfg.extraOptions);
|
||||
DynamicUser = true;
|
||||
StateDirectory = "music-assistant";
|
||||
AmbientCapabilities = "";
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_NETLINK"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged @resources"
|
||||
];
|
||||
RestrictSUIDSGID = true;
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@ -598,6 +598,7 @@ in {
|
||||
# Fails on aarch64-linux at the PDF creation step - need to debug this on an
|
||||
# aarch64 machine..
|
||||
musescore = handleTestOn ["x86_64-linux"] ./musescore.nix {};
|
||||
music-assistant = runTest ./music-assistant.nix;
|
||||
munin = handleTest ./munin.nix {};
|
||||
mutableUsers = handleTest ./mutable-users.nix {};
|
||||
mycelium = handleTest ./mycelium {};
|
||||
|
21
nixos/tests/music-assistant.nix
Normal file
21
nixos/tests/music-assistant.nix
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
name = "music-assistant";
|
||||
meta.maintainers = with lib.maintainers; [ hexa ];
|
||||
|
||||
nodes.machine = {
|
||||
services.music-assistant = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("music-assistant.service")
|
||||
machine.wait_until_succeeds("curl --fail http://localhost:8095")
|
||||
machine.log(machine.succeed("systemd-analyze security music-assistant.service | grep -v ✓"))
|
||||
'';
|
||||
}
|
71
pkgs/by-name/mu/music-assistant/ffmpeg.patch
Normal file
71
pkgs/by-name/mu/music-assistant/ffmpeg.patch
Normal file
@ -0,0 +1,71 @@
|
||||
diff --git a/music_assistant/server/helpers/audio.py b/music_assistant/server/helpers/audio.py
|
||||
index 42011923..1e5dc112 100644
|
||||
--- a/music_assistant/server/helpers/audio.py
|
||||
+++ b/music_assistant/server/helpers/audio.py
|
||||
@@ -218,7 +218,7 @@ async def crossfade_pcm_parts(
|
||||
await outfile.write(fade_out_part)
|
||||
args = [
|
||||
# generic args
|
||||
- "ffmpeg",
|
||||
+ "@ffmpeg@",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"quiet",
|
||||
@@ -281,7 +281,7 @@ async def strip_silence(
|
||||
) -> bytes:
|
||||
"""Strip silence from begin or end of pcm audio using ffmpeg."""
|
||||
fmt = ContentType.from_bit_depth(bit_depth)
|
||||
- args = ["ffmpeg", "-hide_banner", "-loglevel", "quiet"]
|
||||
+ args = ["@ffmpeg@", "-hide_banner", "-loglevel", "quiet"]
|
||||
args += [
|
||||
"-acodec",
|
||||
fmt.name.lower(),
|
||||
@@ -823,7 +823,7 @@ async def get_ffmpeg_stream(
|
||||
async def check_audio_support() -> tuple[bool, bool, str]:
|
||||
"""Check if ffmpeg is present (with/without libsoxr support)."""
|
||||
# check for FFmpeg presence
|
||||
- returncode, output = await check_output("ffmpeg -version")
|
||||
+ returncode, output = await check_output("@ffmpeg@ -version")
|
||||
ffmpeg_present = returncode == 0 and "FFmpeg" in output.decode()
|
||||
|
||||
# use globals as in-memory cache
|
||||
@@ -877,7 +877,7 @@ async def get_silence(
|
||||
return
|
||||
# use ffmpeg for all other encodings
|
||||
args = [
|
||||
- "ffmpeg",
|
||||
+ "@ffmpeg@",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"quiet",
|
||||
@@ -971,7 +971,7 @@ def get_ffmpeg_args(
|
||||
|
||||
# generic args
|
||||
generic_args = [
|
||||
- "ffmpeg",
|
||||
+ "@ffmpeg@",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
loglevel,
|
||||
diff --git a/music_assistant/server/helpers/tags.py b/music_assistant/server/helpers/tags.py
|
||||
index dc38e4c0..f4f3e2fe 100644
|
||||
--- a/music_assistant/server/helpers/tags.py
|
||||
+++ b/music_assistant/server/helpers/tags.py
|
||||
@@ -368,7 +368,7 @@ async def parse_tags(
|
||||
file_path = input_file if isinstance(input_file, str) else "-"
|
||||
|
||||
args = (
|
||||
- "ffprobe",
|
||||
+ "@ffprobe@",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"fatal",
|
||||
@@ -440,7 +440,7 @@ async def get_embedded_image(input_file: str | AsyncGenerator[bytes, None]) -> b
|
||||
"""
|
||||
file_path = input_file if isinstance(input_file, str) else "-"
|
||||
args = (
|
||||
- "ffmpeg",
|
||||
+ "@ffmpeg@",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
35
pkgs/by-name/mu/music-assistant/frontend.nix
Normal file
35
pkgs/by-name/mu/music-assistant/frontend.nix
Normal file
@ -0,0 +1,35 @@
|
||||
{ lib
|
||||
, buildPythonPackage
|
||||
, fetchPypi
|
||||
, setuptools
|
||||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "music-assistant-frontend";
|
||||
version = "2.5.15";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
hash = "sha256-D8VFdXgaVXSxk7c24kvb9TflFztS1zLwW4qGqV32nLo=";
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace pyproject.toml \
|
||||
--replace-fail "~=" ">="
|
||||
'';
|
||||
|
||||
build-system = [ setuptools ];
|
||||
|
||||
doCheck = false;
|
||||
|
||||
pythonImportsCheck = [ "music_assistant_frontend" ];
|
||||
|
||||
meta = with lib; {
|
||||
changelog = "https://github.com/music-assistant/frontend/releases/tag/${version}";
|
||||
description = "The Music Assistant frontend";
|
||||
homepage = "https://github.com/music-assistant/frontend";
|
||||
license = licenses.asl20;
|
||||
maintainers = with maintainers; [ hexa ];
|
||||
};
|
||||
}
|
119
pkgs/by-name/mu/music-assistant/package.nix
Normal file
119
pkgs/by-name/mu/music-assistant/package.nix
Normal file
@ -0,0 +1,119 @@
|
||||
{ lib
|
||||
, python3
|
||||
, fetchFromGitHub
|
||||
, ffmpeg-headless
|
||||
, nixosTests
|
||||
, substituteAll
|
||||
, providers ? [ ]
|
||||
}:
|
||||
|
||||
let
|
||||
python = python3.override {
|
||||
packageOverrides = self: super: {
|
||||
music-assistant-frontend = self.callPackage ./frontend.nix { };
|
||||
};
|
||||
};
|
||||
|
||||
providerPackages = (import ./providers.nix).providers;
|
||||
providerNames = lib.attrNames providerPackages;
|
||||
providerDependencies = lib.concatMap (provider: (providerPackages.${provider} python.pkgs)) providers;
|
||||
|
||||
pythonPath = python.pkgs.makePythonPath providerDependencies;
|
||||
in
|
||||
|
||||
python.pkgs.buildPythonApplication rec {
|
||||
pname = "music-assistant";
|
||||
version = "2.0.7";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "music-assistant";
|
||||
repo = "server";
|
||||
rev = version;
|
||||
hash = "sha256-JtdlZ3hH4fRU5TjmMUlrdSSCnLrIGCuSwSSrnLgjYEs=";
|
||||
};
|
||||
|
||||
patches = [
|
||||
(substituteAll {
|
||||
src = ./ffmpeg.patch;
|
||||
ffmpeg = "${lib.getBin ffmpeg-headless}/bin/ffmpeg";
|
||||
ffprobe = "${lib.getBin ffmpeg-headless}/bin/ffprobe";
|
||||
})
|
||||
];
|
||||
|
||||
postPatch = ''
|
||||
sed -i "/--cov/d" pyproject.toml
|
||||
|
||||
substituteInPlace pyproject.toml \
|
||||
--replace-fail "0.0.0" "${version}"
|
||||
'';
|
||||
|
||||
build-system = with python.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python.pkgs; [
|
||||
aiohttp
|
||||
mashumaro
|
||||
orjson
|
||||
] ++ optional-dependencies.server;
|
||||
|
||||
optional-dependencies = with python.pkgs; {
|
||||
server = [
|
||||
aiodns
|
||||
aiofiles
|
||||
aiohttp
|
||||
aiorun
|
||||
aiosqlite
|
||||
asyncio-throttle
|
||||
brotli
|
||||
certifi
|
||||
colorlog
|
||||
cryptography
|
||||
faust-cchardet
|
||||
ifaddr
|
||||
mashumaro
|
||||
memory-tempfile
|
||||
music-assistant-frontend
|
||||
orjson
|
||||
pillow
|
||||
python-slugify
|
||||
shortuuid
|
||||
unidecode
|
||||
xmltodict
|
||||
zeroconf
|
||||
];
|
||||
};
|
||||
|
||||
nativeCheckInputs = with python.pkgs; [
|
||||
ffmpeg-headless
|
||||
pytest-aiohttp
|
||||
pytestCheckHook
|
||||
] ++ lib.flatten (lib.attrValues optional-dependencies);
|
||||
|
||||
pythonImportsCheck = [ "music_assistant" ];
|
||||
|
||||
passthru = {
|
||||
inherit
|
||||
python
|
||||
pythonPath
|
||||
providerPackages
|
||||
providerNames
|
||||
;
|
||||
tests = nixosTests.music-assistant;
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
changelog = "https://github.com/music-assistant/server/releases/tag/${version}";
|
||||
description = "Music Assistant is a music library manager for various music sources which can easily stream to a wide range of supported players";
|
||||
longDescription = ''
|
||||
Music Assistant is a free, opensource Media library manager that connects to your streaming services and a wide
|
||||
range of connected speakers. The server is the beating heart, the core of Music Assistant and must run on an
|
||||
always-on device like a Raspberry Pi, a NAS or an Intel NUC or alike.
|
||||
'';
|
||||
homepage = "https://github.com/music-assistant/server";
|
||||
license = licenses.asl20;
|
||||
maintainers = with maintainers; [ hexa ];
|
||||
mainProgram = "mass";
|
||||
};
|
||||
}
|
78
pkgs/by-name/mu/music-assistant/providers.nix
Normal file
78
pkgs/by-name/mu/music-assistant/providers.nix
Normal file
@ -0,0 +1,78 @@
|
||||
# Do not edit manually, run ./update-providers.py
|
||||
|
||||
{
|
||||
version = "2.0.7";
|
||||
providers = {
|
||||
airplay = [
|
||||
];
|
||||
builtin = [
|
||||
];
|
||||
chromecast = ps: with ps; [
|
||||
pychromecast
|
||||
];
|
||||
deezer = ps: with ps; [
|
||||
pycryptodome
|
||||
]; # missing deezer-python-async
|
||||
dlna = ps: with ps; [
|
||||
async-upnp-client
|
||||
];
|
||||
fanarttv = [
|
||||
];
|
||||
filesystem_local = [
|
||||
];
|
||||
filesystem_smb = [
|
||||
];
|
||||
fully_kiosk = ps: with ps; [
|
||||
python-fullykiosk
|
||||
];
|
||||
hass = [
|
||||
]; # missing hass-client
|
||||
hass_players = [
|
||||
];
|
||||
jellyfin = [
|
||||
]; # missing jellyfin_apiclient_python
|
||||
musicbrainz = [
|
||||
];
|
||||
opensubsonic = ps: with ps; [
|
||||
py-opensonic
|
||||
];
|
||||
plex = ps: with ps; [
|
||||
plexapi
|
||||
];
|
||||
qobuz = [
|
||||
];
|
||||
radiobrowser = ps: with ps; [
|
||||
radios
|
||||
];
|
||||
slimproto = ps: with ps; [
|
||||
aioslimproto
|
||||
];
|
||||
snapcast = ps: with ps; [
|
||||
snapcast
|
||||
];
|
||||
sonos = ps: with ps; [
|
||||
defusedxml
|
||||
soco
|
||||
sonos-websocket
|
||||
];
|
||||
soundcloud = [
|
||||
]; # missing soundcloudpy
|
||||
spotify = [
|
||||
];
|
||||
test = [
|
||||
];
|
||||
theaudiodb = [
|
||||
];
|
||||
tidal = ps: with ps; [
|
||||
tidalapi
|
||||
];
|
||||
tunein = [
|
||||
];
|
||||
ugp = [
|
||||
];
|
||||
ytmusic = ps: with ps; [
|
||||
pytube
|
||||
ytmusicapi
|
||||
];
|
||||
};
|
||||
}
|
218
pkgs/by-name/mu/music-assistant/update-providers.py
Executable file
218
pkgs/by-name/mu/music-assistant/update-providers.py
Executable file
@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ jinja2 mashumaro orjson aiofiles packaging ])" -p pyright ruff isort
|
||||
import asyncio
|
||||
import json
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
from dataclasses import dataclass, field
|
||||
from functools import cache
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from subprocess import check_output, run
|
||||
from typing import Dict, Final, List, Optional, Set, Union, cast
|
||||
from urllib.request import urlopen
|
||||
|
||||
from jinja2 import Environment
|
||||
from packaging.requirements import Requirement
|
||||
|
||||
TEMPLATE = """# Do not edit manually, run ./update-providers.py
|
||||
|
||||
{
|
||||
version = "{{ version }}";
|
||||
providers = {
|
||||
{%- for provider in providers | sort(attribute='domain') %}
|
||||
{{ provider.domain }} = {% if provider.available %}ps: with ps; {% endif %}[
|
||||
{%- for requirement in provider.available | sort %}
|
||||
{{ requirement }}
|
||||
{%- endfor %}
|
||||
];{% if provider.missing %} # missing {{ ", ".join(provider.missing) }}{% endif %}
|
||||
{%- endfor %}
|
||||
};
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
ROOT: Final = (
|
||||
check_output(
|
||||
[
|
||||
"git",
|
||||
"rev-parse",
|
||||
"--show-toplevel",
|
||||
]
|
||||
)
|
||||
.decode()
|
||||
.strip()
|
||||
)
|
||||
|
||||
PACKAGE_MAP = {
|
||||
"git+https://github.com/MarvinSchenkel/pytube.git": "pytube",
|
||||
}
|
||||
|
||||
|
||||
def run_sync(cmd: List[str]) -> None:
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
process = run(cmd)
|
||||
|
||||
if process.returncode != 0:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
async def check_async(cmd: List[str]) -> str:
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode != 0:
|
||||
error = stderr.decode()
|
||||
raise RuntimeError(f"{cmd[0]} failed: {error}")
|
||||
|
||||
return stdout.decode().strip()
|
||||
|
||||
|
||||
class Nix:
|
||||
base_cmd: Final = [
|
||||
"nix",
|
||||
"--show-trace",
|
||||
"--extra-experimental-features",
|
||||
"nix-command",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
async def _run(cls, args: List[str]) -> Optional[str]:
|
||||
return await check_async(cls.base_cmd + args)
|
||||
|
||||
@classmethod
|
||||
async def eval(cls, expr: str) -> Union[List, Dict, int, float, str, bool]:
|
||||
response = await cls._run(["eval", "-f", f"{ROOT}/default.nix", "--json", expr])
|
||||
if response is None:
|
||||
raise RuntimeError("Nix eval expression returned no response")
|
||||
try:
|
||||
return json.loads(response)
|
||||
except (TypeError, ValueError):
|
||||
raise RuntimeError("Nix eval response could not be parsed from JSON")
|
||||
|
||||
|
||||
async def get_provider_manifests(version: str = "master") -> List:
|
||||
manifests = []
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
with urlopen(
|
||||
f"https://github.com/music-assistant/music-assistant/archive/{version}.tar.gz"
|
||||
) as response:
|
||||
tarfile.open(fileobj=BytesIO(response.read())).extractall(
|
||||
tmp, filter="data"
|
||||
)
|
||||
|
||||
basedir = Path(os.path.join(tmp, f"server-{version}"))
|
||||
sys.path.append(str(basedir))
|
||||
from music_assistant.common.models.provider import ProviderManifest # type: ignore
|
||||
|
||||
for fn in basedir.glob("**/manifest.json"):
|
||||
manifests.append(await ProviderManifest.parse(fn))
|
||||
|
||||
return manifests
|
||||
|
||||
|
||||
@cache
|
||||
def packageset_attributes():
|
||||
output = check_output(
|
||||
[
|
||||
"nix-env",
|
||||
"-f",
|
||||
ROOT,
|
||||
"-qa",
|
||||
"-A",
|
||||
"music-assistant.python.pkgs",
|
||||
"--arg",
|
||||
"config",
|
||||
"{ allowAliases = false; }",
|
||||
"--json",
|
||||
]
|
||||
)
|
||||
return json.loads(output)
|
||||
|
||||
|
||||
class TooManyMatches(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NoMatch(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def resolve_package_attribute(package: str) -> str:
|
||||
pattern = re.compile(rf"^music-assistant\.python\.pkgs\.{package}$", re.I)
|
||||
packages = packageset_attributes()
|
||||
matches = []
|
||||
for attr in packages.keys():
|
||||
if pattern.match(attr):
|
||||
matches.append(attr.split(".")[-1])
|
||||
|
||||
if len(matches) > 1:
|
||||
raise TooManyMatches(
|
||||
f"Too many matching attributes for {package}: {' '.join(matches)}"
|
||||
)
|
||||
if not matches:
|
||||
raise NoMatch(f"No matching attribute for {package}")
|
||||
|
||||
return matches.pop()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Provider:
|
||||
domain: str
|
||||
available: list[str] = field(default_factory=list)
|
||||
missing: list[str] = field(default_factory=list)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.domain == other.domain
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.domain)
|
||||
|
||||
|
||||
def resolve_providers(manifests) -> Set:
|
||||
providers = set()
|
||||
for manifest in manifests:
|
||||
provider = Provider(manifest.domain)
|
||||
for requirement in manifest.requirements:
|
||||
# allow substituting requirement specifications that packaging cannot parse
|
||||
if requirement in PACKAGE_MAP:
|
||||
requirement = PACKAGE_MAP[requirement]
|
||||
requirement = Requirement(requirement)
|
||||
try:
|
||||
provider.available.append(resolve_package_attribute(requirement.name))
|
||||
except TooManyMatches as ex:
|
||||
print(ex, file=sys.stderr)
|
||||
provider.missing.append(requirement.name)
|
||||
except NoMatch:
|
||||
provider.missing.append(requirement.name)
|
||||
providers.add(provider)
|
||||
return providers
|
||||
|
||||
|
||||
def render(version: str, providers: Set):
|
||||
path = os.path.join(ROOT, "pkgs/by-name/mu/music-assistant/providers.nix")
|
||||
env = Environment()
|
||||
template = env.from_string(TEMPLATE)
|
||||
template.stream(version=version, providers=providers).dump(path)
|
||||
|
||||
|
||||
async def main():
|
||||
version: str = cast(str, await Nix.eval("music-assistant.version"))
|
||||
manifests = await get_provider_manifests(version)
|
||||
providers = resolve_providers(manifests)
|
||||
render(version, providers)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_sync(["pyright", __file__])
|
||||
run_sync(["ruff", "check", "--ignore=E501", __file__])
|
||||
run_sync(["isort", __file__])
|
||||
run_sync(["ruff", "format", __file__])
|
||||
asyncio.run(main())
|
42
pkgs/development/python-modules/memory-tempfile/default.nix
Normal file
42
pkgs/development/python-modules/memory-tempfile/default.nix
Normal file
@ -0,0 +1,42 @@
|
||||
{ lib
|
||||
, buildPythonPackage
|
||||
, fetchFromGitHub
|
||||
, fetchpatch2
|
||||
, poetry-core
|
||||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "memory-tempfile";
|
||||
version = "2.2.3";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "mbello";
|
||||
repo = "memory-tempfile";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-4fz2CLkZdy2e1GwGw/afG54LkUVJ4cza70jcbX3rVlQ=";
|
||||
};
|
||||
|
||||
patches = [
|
||||
(fetchpatch2 {
|
||||
# Migrate to poetry-core build backend
|
||||
# https://github.com/mbello/memory-tempfile/pull/13
|
||||
name = "poetry-core.patch";
|
||||
url = "https://github.com/mbello/memory-tempfile/commit/938a3a3abf01756b1629eca6c69e970021bbc7c0.patch";
|
||||
hash = "sha256-q3027MwKXtX09MH7T2UrX19BImK1FJo+YxADfxcdTME=";
|
||||
})
|
||||
];
|
||||
|
||||
build-system = [ poetry-core ];
|
||||
|
||||
doCheck = false; # constrained selection of memory backed filesystems due to build sandbox
|
||||
|
||||
pythonImportsCheck = [ "memory_tempfile" ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Create temporary files and temporary dirs in memory-based filesystems on Linux";
|
||||
homepage = "https://github.com/mbello/memory-tempfile";
|
||||
license = licenses.mit;
|
||||
maintainers = with maintainers; [ hexa ];
|
||||
};
|
||||
}
|
37
pkgs/development/python-modules/py-opensonic/default.nix
Normal file
37
pkgs/development/python-modules/py-opensonic/default.nix
Normal file
@ -0,0 +1,37 @@
|
||||
{ lib
|
||||
, buildPythonPackage
|
||||
, fetchFromGitHub
|
||||
, setuptools
|
||||
, requests
|
||||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "py-opensonic";
|
||||
version = "5.1.1";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "khers";
|
||||
repo = "py-opensonic";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-wXTXuX+iIMEoALxsciopucmvBxAyEeiOgjJPrbD63gM=";
|
||||
};
|
||||
|
||||
build-system = [ setuptools ];
|
||||
|
||||
dependencies = [ requests ];
|
||||
|
||||
doCheck = false; # no tests
|
||||
|
||||
pythonImportsCheck = [
|
||||
"libopensonic"
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Python library to wrap the Open Subsonic REST API";
|
||||
homepage = "https://github.com/khers/py-opensonic";
|
||||
changelog = "https://github.com/khers/py-opensonic/blob/${src.rev}/CHANGELOG.md";
|
||||
license = licenses.gpl3Only;
|
||||
maintainers = with maintainers; [ hexa ];
|
||||
};
|
||||
}
|
@ -27,6 +27,9 @@ home-assistant.python.pkgs.buildPythonPackage (
|
||||
mkdir $out
|
||||
cp -r ./custom_components/ $out/
|
||||
|
||||
# optionally copy sentences, if they exist
|
||||
cp -r ./custom_sentences/ $out/ || true
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
localtuya = callPackage ./localtuya {};
|
||||
|
||||
mass = callPackage ./mass { };
|
||||
|
||||
midea_ac_lan = callPackage ./midea_ac_lan {};
|
||||
|
||||
midea-air-appliances-lan = callPackage ./midea-air-appliances-lan {};
|
||||
|
@ -0,0 +1,34 @@
|
||||
{ lib
|
||||
, buildHomeAssistantComponent
|
||||
, fetchFromGitHub
|
||||
, toPythonModule
|
||||
, async-timeout
|
||||
, music-assistant
|
||||
}:
|
||||
|
||||
buildHomeAssistantComponent rec {
|
||||
owner = "music-assistant";
|
||||
domain = "mass";
|
||||
version = "2024.6.2";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "music-assistant";
|
||||
repo = "hass-music-assistant";
|
||||
rev = version;
|
||||
hash = "sha256-Wvc+vUYkUJmS4U34Sh/sDCVXmQA0AtEqIT8MNXd++3M=";
|
||||
};
|
||||
|
||||
dependencies = [
|
||||
async-timeout
|
||||
(toPythonModule music-assistant)
|
||||
];
|
||||
|
||||
dontCheckManifest = true; # expects music-assistant 2.0.6, we have 2.0.7
|
||||
|
||||
meta = with lib; {
|
||||
description = "Turn your Home Assistant instance into a jukebox, hassle free streaming of your favorite media to Home Assistant media players";
|
||||
homepage = "https://github.com/music-assistant/hass-music-assistant";
|
||||
license = licenses.asl20;
|
||||
maintainers = with maintainers; [ hexa ];
|
||||
};
|
||||
}
|
@ -7524,6 +7524,8 @@ self: super: with self; {
|
||||
|
||||
memory-profiler = callPackage ../development/python-modules/memory-profiler { };
|
||||
|
||||
memory-tempfile = callPackage ../development/python-modules/memory-tempfile { };
|
||||
|
||||
meraki = callPackage ../development/python-modules/meraki { };
|
||||
|
||||
mercadopago = callPackage ../development/python-modules/mercadopago { };
|
||||
@ -9195,6 +9197,8 @@ self: super: with self; {
|
||||
|
||||
py-expression-eval = callPackage ../development/python-modules/py-expression-eval { };
|
||||
|
||||
py-opensonic = callPackage ../development/python-modules/py-opensonic { };
|
||||
|
||||
py-radix-sr = callPackage ../development/python-modules/py-radix-sr { };
|
||||
|
||||
nwdiag = callPackage ../development/python-modules/nwdiag { };
|
||||
|
Loading…
Reference in New Issue
Block a user