-
Notifications
You must be signed in to change notification settings - Fork 4k
sendspin-cli: add new package #30577
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
Open
mguaylam
wants to merge
1
commit into
openwrt:master
Choose a base branch
from
mguaylam:sendspin-cli
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| # SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| include $(TOPDIR)/rules.mk | ||
|
|
||
| PKG_NAME:=sendspin-cli | ||
| PKG_VERSION:=0.3.0 | ||
| PKG_RELEASE:=1 | ||
|
|
||
| PKG_SOURCE:=sendspin-cpp-cli-$(PKG_VERSION).tar.gz | ||
| PKG_SOURCE_URL:=https://codeload.github.com/Sendspin/sendspin-cpp-cli/tar.gz/v$(PKG_VERSION)? | ||
| PKG_HASH:=baa9febbc08d58e1780d6a62fb0b20d458cd1a3cbb177929ba5f4ff0b01d335c | ||
| PKG_BUILD_DIR:=$(BUILD_DIR)/sendspin-cpp-cli-$(PKG_VERSION) | ||
|
|
||
| PKG_MAINTAINER:=Michaël Guay-Lambert <mguaylam.dev@gmail.com> | ||
| PKG_LICENSE:=Apache-2.0 BSD-3-Clause MIT | ||
| # The bundled dependencies land under openwrt-deps/ in Build/Prepare below. | ||
| PKG_LICENSE_FILES:=LICENSE \ | ||
| openwrt-deps/sendspin-cpp/LICENSE \ | ||
| openwrt-deps/ArduinoJson/LICENSE.txt \ | ||
| openwrt-deps/IXWebSocket/LICENSE.txt \ | ||
| openwrt-deps/micro-flac/LICENSE | ||
|
|
||
| PKG_BUILD_FLAGS:=no-mips16 | ||
|
|
||
| # The versions sendspin-cli v$(PKG_VERSION) pins through FetchContent | ||
| # (SENDSPIN_GIT_TAG, then sendspin-cpp's cmake/host.cmake). | ||
| SENDSPIN_CPP_VERSION:=0.8.0 | ||
| ARDUINOJSON_VERSION:=7.4.1 | ||
| IXWEBSOCKET_VERSION:=11.4.5 | ||
| MICRO_FLAC_VERSION:=0.1.1 | ||
|
|
||
| include $(INCLUDE_DIR)/package.mk | ||
| include $(INCLUDE_DIR)/cmake.mk | ||
|
|
||
| define Package/sendspin-cli | ||
| SECTION:=sound | ||
| CATEGORY:=Sound | ||
| TITLE:=Sendspin synchronized audio player | ||
| URL:=https://github.com/Sendspin/sendspin-cpp-cli | ||
| # Not offered on big-endian: software volume, the Opus decoder and | ||
| # micro-flac's sample packing write samples in host byte order into buffers | ||
| # the ALSA sink opens as _LE. Reproduced under qemu-mips and reported as | ||
| # Sendspin/sendspin-cpp-cli#70, Sendspin/sendspin-cpp#132 and | ||
| # esphome-libs/micro-flac#36. Drop once they are fixed. | ||
| DEPENDS:=+alsa-lib +libatomic +libopus +libstdcpp +umdns @!BIG_ENDIAN | ||
| USERID:=sendspin:audio=29 | ||
| endef | ||
|
|
||
| define Package/sendspin-cli/description | ||
| Headless player for Sendspin, the synchronized multi-room audio protocol | ||
| used by Music Assistant. It plays through ALSA and is announced over mDNS | ||
| so a Sendspin server can discover it. | ||
| endef | ||
|
|
||
| define Package/sendspin-cli/conffiles | ||
| /etc/config/sendspin-cli | ||
| endef | ||
|
|
||
| define Download/sendspin-cpp | ||
| URL:=https://codeload.github.com/Sendspin/sendspin-cpp/tar.gz/v$(SENDSPIN_CPP_VERSION)? | ||
| FILE:=sendspin-cpp-$(SENDSPIN_CPP_VERSION).tar.gz | ||
| HASH:=cfd485ca5ecaff2406af382f309ce7d0c8709d56ac6c0a3a7420f8d423978a02 | ||
| endef | ||
| $(eval $(call Download,sendspin-cpp)) | ||
|
|
||
| define Download/arduinojson | ||
| URL:=https://codeload.github.com/bblanchon/ArduinoJson/tar.gz/v$(ARDUINOJSON_VERSION)? | ||
| FILE:=ArduinoJson-$(ARDUINOJSON_VERSION).tar.gz | ||
| HASH:=4db7245408c58c4869c135aece5e92c784d8026f5dbc6efd0485e52b84264d10 | ||
| endef | ||
| $(eval $(call Download,arduinojson)) | ||
|
|
||
| define Download/ixwebsocket | ||
| URL:=https://codeload.github.com/machinezone/IXWebSocket/tar.gz/v$(IXWEBSOCKET_VERSION)? | ||
| FILE:=IXWebSocket-$(IXWEBSOCKET_VERSION).tar.gz | ||
| HASH:=c5fc225edec32bf7d583e55347ef2c9c4940d005c13ef5e848354a85602f5fd6 | ||
| endef | ||
| $(eval $(call Download,ixwebsocket)) | ||
|
|
||
| define Download/micro-flac | ||
| URL:=https://codeload.github.com/esphome-libs/micro-flac/tar.gz/v$(MICRO_FLAC_VERSION)? | ||
| FILE:=micro-flac-$(MICRO_FLAC_VERSION).tar.gz | ||
| HASH:=4cc9c69f1ec06e7d0a7bd116bc61cc3f398d056eb9b2e9402a8a4790e307e854 | ||
| endef | ||
| $(eval $(call Download,micro-flac)) | ||
|
|
||
| DEPS_DIR:=$(PKG_BUILD_DIR)/openwrt-deps | ||
|
|
||
| # $(1): directory under $(DEPS_DIR), $(2): archive in $(DL_DIR) | ||
| define Prepare/dependency | ||
| mkdir -p $(DEPS_DIR)/$(1) | ||
| $(HOST_TAR) -C $(DEPS_DIR)/$(1) --strip-components=1 -xzf $(DL_DIR)/$(2) | ||
| endef | ||
|
|
||
| define Build/Prepare | ||
| $(call Build/Prepare/Default) | ||
| $(call Prepare/dependency,sendspin-cpp,sendspin-cpp-$(SENDSPIN_CPP_VERSION).tar.gz) | ||
| $(call Prepare/dependency,ArduinoJson,ArduinoJson-$(ARDUINOJSON_VERSION).tar.gz) | ||
| $(call Prepare/dependency,IXWebSocket,IXWebSocket-$(IXWEBSOCKET_VERSION).tar.gz) | ||
| $(call Prepare/dependency,micro-flac,micro-flac-$(MICRO_FLAC_VERSION).tar.gz) | ||
| $(CP) ./cmake/micro-opus $(DEPS_DIR)/ | ||
| endef | ||
|
|
||
| # Hand every FetchContent dependency a local source tree and forbid any | ||
| # download. micro-opus is replaced by a shim over the feed's libopus, and | ||
| # micro-flac's Ogg container support, which Sendspin does not use, is left out | ||
| # along with the micro-ogg-demuxer submodule it needs. | ||
| CMAKE_OPTIONS += \ | ||
| -DFETCHCONTENT_FULLY_DISCONNECTED=ON \ | ||
| -DFETCHCONTENT_SOURCE_DIR_SENDSPIN=$(DEPS_DIR)/sendspin-cpp \ | ||
| -DFETCHCONTENT_SOURCE_DIR_ARDUINOJSON=$(DEPS_DIR)/ArduinoJson \ | ||
| -DFETCHCONTENT_SOURCE_DIR_IXWEBSOCKET=$(DEPS_DIR)/IXWebSocket \ | ||
| -DFETCHCONTENT_SOURCE_DIR_MICRO_FLAC=$(DEPS_DIR)/micro-flac \ | ||
| -DFETCHCONTENT_SOURCE_DIR_MICRO_OPUS=$(DEPS_DIR)/micro-opus \ | ||
| -DMICRO_FLAC_ENABLE_OGG=OFF \ | ||
| -DSENDSPIN_CLI_BUILD_TESTS=OFF \ | ||
| -DSENDSPIN_CLI_WITH_ALSA=ON \ | ||
| -DSENDSPIN_CLI_WITH_MDNS=OFF \ | ||
| -DSENDSPIN_CLI_WITH_PIPEWIRE=OFF \ | ||
| -DSENDSPIN_CLI_WITH_PORTAUDIO=OFF \ | ||
| -DSENDSPIN_CLI_WITH_PULSE=OFF \ | ||
| -DSENDSPIN_CLI_WERROR=OFF | ||
|
|
||
| # 64-bit std::atomic needs libatomic on 32-bit targets without those ops | ||
| TARGET_LDFLAGS += -latomic | ||
|
|
||
| define Package/sendspin-cli/install | ||
| $(INSTALL_DIR) $(1)/usr/bin | ||
| $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/sendspin-cli $(1)/usr/bin/ | ||
| $(INSTALL_DIR) $(1)/etc/config | ||
| $(INSTALL_CONF) ./files/sendspin-cli.config $(1)/etc/config/sendspin-cli | ||
| $(INSTALL_DIR) $(1)/etc/init.d | ||
| $(INSTALL_BIN) ./files/sendspin-cli.init $(1)/etc/init.d/sendspin-cli | ||
| $(INSTALL_DIR) $(1)/etc/hotplug.d/iface | ||
| $(INSTALL_DATA) ./files/sendspin-cli.hotplug $(1)/etc/hotplug.d/iface/50-sendspin-cli | ||
| endef | ||
|
|
||
| $(eval $(call BuildPackage,sendspin-cli)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Stands in for esphome-libs/micro-opus, which sendspin-cpp fetches to get a | ||
| # bundled copy of Opus. sendspin-cpp only links a target named micro_opus and | ||
| # includes <opus.h>, so the libopus from the OpenWrt feed is enough. | ||
| cmake_minimum_required(VERSION 3.16) | ||
| project(micro_opus LANGUAGES C) | ||
|
|
||
| find_package(PkgConfig REQUIRED) | ||
| pkg_check_modules(OPUS REQUIRED IMPORTED_TARGET GLOBAL opus) | ||
|
|
||
| add_library(micro_opus INTERFACE) | ||
| target_link_libraries(micro_opus INTERFACE PkgConfig::OPUS) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| config player 'main' | ||
| option enabled '0' | ||
| # Name shown by the Sendspin server; empty uses the router's hostname | ||
| option name '' | ||
| # ALSA device; a hw: device plays without resampling. | ||
| # 'sendspin-cli -l' lists the devices and the formats they accept. | ||
| option device 'hw:0,0' | ||
| # Formats offered first, as a comma-separated list of | ||
| # codec:rate:depth:channels, e.g. flac:48000:16:2,pcm:48000:16:2. The rest | ||
| # of the device's formats stay behind them, so this is a preference the | ||
| # server can fall back from. Empty offers everything the device accepts. | ||
| option audio_format '' | ||
| # Audio kept buffered by ALSA, in milliseconds (10-2000) | ||
| option buffer_ms '100' | ||
| # Latency added after the DAC (amplifier, DSP), in milliseconds. | ||
| # Only a first-run default: a delay set from the server is remembered. | ||
| option static_delay '0' | ||
| # Port the player listens on for the server | ||
| option port '8928' | ||
| # Announce the player over mDNS so the server discovers it | ||
| option mdns '1' | ||
| # Connect out to host[:port] instead of waiting to be discovered; | ||
| # disables the mDNS announcement | ||
| option server '' | ||
| # none, error, warn, info, debug or verbose | ||
| option log_level 'info' | ||
| # Volume, mute, static delay and last server survive restarts here. | ||
| # The default is in RAM, so they are lost at reboot but never wear the flash. | ||
| option state_dir '/var/lib/sendspin-cli' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| #!/bin/sh | ||
|
|
||
| # umdns does not announce service instances when a network comes up or changes | ||
| # address, and on a network with an mDNS reflector it takes its own reflected | ||
| # probe for a name conflict and stops announcing. It does announce a service | ||
| # that is added, so restart the player to withdraw and add it again. | ||
| # Remove once openwrt/mdnsd#36 is in the umdns shipped by OpenWrt. | ||
|
|
||
| case "$ACTION" in | ||
| ifup) ;; | ||
| ifupdate) [ -n "$IFUPDATE_ADDRESSES" ] || exit 0 ;; | ||
| *) exit 0 ;; | ||
| esac | ||
|
|
||
| for network in $(uci -q get umdns.@umdns[-1].network); do | ||
| [ "$network" = "$INTERFACE" ] || continue | ||
| /etc/init.d/sendspin-cli running || exit 0 | ||
| # Only a player announced over mDNS has anything to refresh; one | ||
| # connecting out to a server would just lose its stream. | ||
| ubus call service list '{"name":"sendspin-cli"}' 2> /dev/null | | ||
| jsonfilter -e '@["sendspin-cli"].instances[*].data.mdns' > /dev/null 2>&1 || exit 0 | ||
| logger -t sendspin-cli "Restarting to announce on $INTERFACE after $ACTION" | ||
| /etc/init.d/sendspin-cli restart > /dev/null 2>&1 & | ||
| exit 0 | ||
| done |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| #!/bin/sh /etc/rc.common | ||
|
|
||
| START=99 | ||
| STOP=10 | ||
|
|
||
| USE_PROCD=1 | ||
|
|
||
| PROG=/usr/bin/sendspin-cli | ||
| RUN_DIR=/var/run/sendspin-cli | ||
|
|
||
| validate_player_section() { | ||
| uci_load_validate sendspin-cli player "$1" "$2" \ | ||
| 'enabled:bool:0' \ | ||
| 'name:string' \ | ||
| 'device:string:hw:0,0' \ | ||
| 'audio_format:string' \ | ||
| 'buffer_ms:range(10,2000):100' \ | ||
| 'static_delay:uinteger:0' \ | ||
| 'port:port:8928' \ | ||
| 'mdns:bool:1' \ | ||
| 'server:string' \ | ||
| 'log_level:or("none","error","warn","info","debug","verbose"):info' \ | ||
| 'state_dir:string:/var/lib/sendspin-cli' | ||
| } | ||
|
|
||
| start_player() { | ||
| [ "$2" = 0 ] || { | ||
| echo "validation failed" | ||
| return 1 | ||
| } | ||
|
|
||
| [ "$enabled" = 1 ] || return 0 | ||
|
|
||
| [ -n "$name" ] || name="$(cat /proc/sys/kernel/hostname)" | ||
|
|
||
| mkdir -p "$state_dir" "$RUN_DIR" | ||
| chown sendspin:audio "$state_dir" "$RUN_DIR" | ||
|
|
||
| procd_open_instance "$1" | ||
| procd_set_param command "$PROG" \ | ||
| --name "$name" \ | ||
| --output "$device" \ | ||
| --buffer-ms "$buffer_ms" \ | ||
| --static-delay "$static_delay" \ | ||
| --port "$port" \ | ||
| --log-level "$log_level" \ | ||
| --state-dir "$state_dir" \ | ||
| --control-socket "$RUN_DIR/$1.sock" \ | ||
| --no-mdns | ||
| [ -n "$audio_format" ] && procd_append_param command --audio-format "$audio_format" | ||
|
|
||
| # The player is built without mDNS; umdns announces it instead | ||
| if [ -n "$server" ]; then | ||
| procd_append_param command --server "$server" | ||
| elif [ "$mdns" = 1 ]; then | ||
| procd_add_mdns "sendspin" "tcp" "$port" "path=/sendspin" "name=$name" | ||
| fi | ||
|
|
||
| procd_set_param user sendspin | ||
| procd_set_param group audio | ||
| procd_set_param nice -10 | ||
| procd_set_param respawn | ||
| procd_set_param stderr 1 | ||
| procd_close_instance | ||
| } | ||
|
|
||
| start_service() { | ||
| config_load sendspin-cli | ||
| config_foreach validate_player_section player start_player | ||
| } | ||
|
|
||
| # The player says goodbye with reason 'shutdown' whenever it stops, and a | ||
| # server takes that at its word: it reconnects only once mDNS announces the | ||
| # player again. umdns announces a service when it appears and withdraws it when | ||
| # it disappears, but a restart that swaps it for an identical one does neither. | ||
| # So withdraw the announcement once the player has actually exited; umdns | ||
| # announces it again a few seconds after it starts. A configuration reload is | ||
| # a full restart for the same reason. | ||
| service_stopped() { | ||
| local wait=10 | ||
|
|
||
| while procd_running sendspin-cli && [ "$wait" -gt 0 ]; do | ||
| sleep 1 | ||
| wait=$((wait - 1)) | ||
| done | ||
| ubus -t 2 call umdns reload > /dev/null 2>&1 | ||
| } | ||
|
|
||
| reload_service() { | ||
| restart | ||
| } | ||
|
|
||
| service_triggers() { | ||
| procd_add_reload_trigger sendspin-cli | ||
| procd_add_validation validate_player_section | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| #!/bin/sh | ||
|
|
||
| # shellcheck shell=busybox | ||
|
|
||
| # Start the player on the null output and check it comes up and listens, | ||
| # which the generic version check alone does not show. | ||
|
|
||
| [ "$1" = sendspin-cli ] || exit 0 | ||
|
|
||
| state=$(mktemp -d) | ||
| pid="" | ||
| cleanup() { [ -n "$pid" ] && kill "$pid" 2>/dev/null; rm -rf "$state"; } | ||
| trap cleanup EXIT | ||
|
|
||
| sendspin-cli --output null --name ci --no-mdns --no-control --state-dir "$state" > "$state/log" 2>&1 & | ||
| pid=$! | ||
|
|
||
| tries=30 | ||
| while [ "$tries" -gt 0 ] && ! grep -F "listening on port" "$state/log" > /dev/null; do | ||
| sleep 1 | ||
| tries=$((tries - 1)) | ||
| done | ||
|
|
||
| kill "$pid" | ||
| wait "$pid" | ||
| cat "$state/log" | ||
| grep -F "sendspin-cli $2 listening on port 8928" "$state/log" | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the
mktemp -ddirectory is never removed, so each run leaks one.libs/redis/test.shtraps EXIT for this.Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, and I followed the redis precedent a bit further than the suggestion. Its
cleanupalso kills the server, which matters here for the same reason: if CI interrupts the script between the launch and thekill, the player is left running as well as the directory left behind.The explicit
kill/waitbefore the finalgrepstays, so the normal path is unchanged and the trap only covers abnormal exits.