diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df1ca71 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +package.tgz +*.spk +LICENSE +/target +/dist diff --git a/INFO b/INFO new file mode 100644 index 0000000..f7d1edc --- /dev/null +++ b/INFO @@ -0,0 +1,9 @@ +package="AirConnect" +version=#VERSION# +description="Use AirPlay to stream to UPnP/Sonos & Chromecast devices" +maintainer="philippe44" +maintainer_url="https://github.com/philippe44/AirConnect" +distributor="eizedev" +distributor_url="https://github.com/eizedev/AirConnect-Synology" +arch="#INFO_ARCH#" +firmware="#INFO_FIRMWARE#" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..294d1f9 --- /dev/null +++ b/Makefile @@ -0,0 +1,87 @@ +#Git commit SHA +REPO_REVISION=cb731dce032268ea93e5a7e6d5b3dc73c95b6c88 +VERSION=0.2.24.7-$(shell date '+%Y%m%d') + +LICENSE: + curl -s -L https://github.com/philippe44/AirConnect/raw/${REPO_REVISION}/LICENSE -O + +target: + mkdir -p target + +dist: + mkdir -p dist + +target/package.tgz: target + $(if ${ARCH},,$(error Must specify ARCH)) + curl -s -L https://github.com/philippe44/AirConnect/raw/${REPO_REVISION}/bin/airupnp-${ARCH} -o target/airupnp + chmod +x target/airupnp + curl -s -L https://github.com/philippe44/AirConnect/raw/${REPO_REVISION}/bin/aircast-${ARCH} -o target/aircast + chmod +x target/aircast + cd target && tar czf package.tgz airupnp aircast + rm target/airupnp target/aircast + +target/scripts: target + cp -a scripts target + +target/LICENSE: target + curl -s -L https://github.com/philippe44/AirConnect/raw/${REPO_REVISION}/LICENSE -o target/LICENSE + +target/PACKAGE_ICON.PNG: target + cp PACKAGE_ICON.PNG target/PACKAGE_ICON.PNG + +target/INFO: target + $(if ${INFO_ARCH},,$(error Must specify INFO_ARCH)) + $(if ${INFO_FIRMWARE},,$(error Must specify INFO_FIRMWARE)) + cp INFO target/INFO + sed -i.bak -e 's/#VERSION#/${VERSION}/' target/INFO + sed -i.bak -e 's/#INFO_ARCH#/${INFO_ARCH}/' target/INFO + sed -i.bak -e 's/#INFO_FIRMWARE#/${INFO_FIRMWARE}/' target/INFO + rm target/INFO.bak + +dist/AirConnect-${ARCH}-${VERSION}.spk: target/package.tgz target/scripts target/LICENSE target/INFO target/PACKAGE_ICON.PNG dist + $(if ${ARCH},,$(error Must specify ARCH)) + cd target && tar -czf AirConnect-${ARCH}-${VERSION}.spk * + mv target/AirConnect-${ARCH}-${VERSION}.spk dist/ + +.PHONY: arm +arm: + $(eval export INFO_ARCH=ipq806x armada370 armadaxp armada375 armada38x alpine alpine4k monaco comcerto2k) + $(eval export INFO_FIRMWARE=5.0-4458) + @true + +.PHONY: aarch64 +aarch64: + $(eval export INFO_ARCH=rtd1296) + $(eval export INFO_FIRMWARE=5.0-4458) + @true + +.PHONY: x86 +x86: + $(eval export INFO_ARCH=x86 cedarview bromolow evansport avoton braswell broadwell apollolake) + $(eval export INFO_FIRMWARE=5.0-4458) + @true + +.PHONY: x86-64 +x86-64: + $(eval export INFO_ARCH=x86_64) + $(eval export INFO_FIRMWARE=6.0-7321) + @true + +.PHONY: build +build: ${ARCH} dist/AirConnect-${ARCH}-${VERSION}.spk + +.PHONY: clean +clean: + rm -rf target + +.PHONY: clean-dist +clean-dist: + rm -rf dist + +.PHONY: build-all +build-all: clean-dist + ./build.sh + +.PHONY: shellcheck +shellcheck: + shellcheck -s sh scripts/* diff --git a/PACKAGE_ICON.PNG b/PACKAGE_ICON.PNG new file mode 100644 index 0000000..417fcca Binary files /dev/null and b/PACKAGE_ICON.PNG differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc96c66 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# AirConnect package for Synology NAS and Synology Router + +[![Latest release](https://img.shields.io/github/release/eizedev/AirConnect-Synology.svg)](https://github.com/eizedev/AirConnect-Synology/releases/latest) + +A minimal Synology package for [AirConnect](https://github.com/philippe44/AirConnect). +It allows you to use AirPlay to stream to UPnP/Sonos & Chromecast devices. + +- [AirConnect package for Synology NAS and Synology Router](#airconnect-package-for-synology-nas-and-synology-router) + - [Information](#information) + - [How to install](#how-to-install) + - [Download the pre-built Synology package](#download-the-pre-built-synology-package) + - [Install via GUI (Package Center)](#install-via-gui-package-center) + - [Install via command line](#install-via-command-line) + - [How it works](#how-it-works) + - [Build](#build) + - [Run shellcheck (optional)](#run-shellcheck-optional) + - [Build packages for all architectures](#build-packages-for-all-architectures) + - [Build a package for a specific architecture](#build-a-package-for-a-specific-architecture) + - [Debugging](#debugging) + - [License](#license) + +## Information + +Since the [original repository](https://github.com/bandesz/AirConnect-Synology) from [@bandesz](https://github.com/bandesz) was archived, I will try to provide the latest releases here regularly and have updated the scripts and just provided the current release. +I own multiple Synology NAS Systems and the current Synology Router, as long as that is the case, I will also update the releases regularly. + +If a release is missing, please open an [issue](https://github.com/eizedev/AirConnect-Synology/issues), then I will deliver it to. + +The credit goes of course still to [@bandesz](https://github.com/bandesz) and [philippe44](https://github.com/philippe44). + +## How to install + +### Download the pre-built Synology package + +You can find the available packages under [Releases](https://github.com/eizedev/AirConnect-Synology/releases) for four different architecture groups: + +- **ARMv7**: ipq806x armada370 armadaxp armada375 armada38x alpine alpine4k monaco comcerto2k +- **ARMv8**: rtd1296 +- **Intel - 32-bit**: x86 cedarview bromolow evansport avoton braswell broadwell apollolake +- **Intel - 64-bit (DSM 6.0+)**: x86_64 + +You can check which architecture you have [here](https://www.synology.com/en-us/knowledgebase/DSM/tutorial/Compatibility_Peripherals/What_kind_of_CPU_does_my_NAS_have). + +For the Synology Routers you should use the ARM version. + +### Install via GUI (Package Center) + +Open the Package Center app. + +As this package is not an official Synology package you may have to **allow packages from any publisher**. Go to **Settings** and set the **Trust Level** to "**Any publisher**". + +Click on **Manual Install** and upload the package you just downloaded. + +Don't forget to **change back** the **Trust level** to "Synology Inc." for additional security. + +You can see the error logs by clicking on **View Log** on the package's page. + +### Install via command line + +- Connect to your NAS/Router via ssh +- Copy over or download your release for [your architecture](#download-the-pre-built-synology-package) +- Run the following command based on your architecture and version `sudo synopkg install AirConnect-${ARCH}-${VERSION}.spk` + - Example: `sudo synopkg install AirConnect-arm-0.2.24.7-20200417.spk` +- Start the **AirConnect** package with `/usr/syno/bin/synopkg start AirConnect` or trough the Package Center +- Check the status of AirConnect with `/usr/syno/bin/synopkg status AirConnect` +- You can find the logfile with `/usr/syno/bin/synopkg log AirConnect` (Default: `/tmp/airconnect.log`) + +You could also clone this repository on your synology device and build your package for your distribution locally, check [Build](#build) for more details. + +## How it works + +It runs the AirConnect processes with the following options: + +```bash +airupnp -b [router local ip]:49154 -z -l 1000:2000 -f /tmp/airupnp.log -d all=error -d main=info + +aircast -b [router local ip] -z -l 1000:2000 -f /tmp/aircast.log -d all=error -d main=info +``` + +The process is running with a low-privilege user. + +The processes will only recognise your devices if they are bound to the appropriate local network IP, but this is not trivial as there are various Synology devices and network setups. +The start script will check all your local network interfaces (with ip 192.168.* or 10.* or 172.16.* - 172.31.*) and checks if the airupnp/aircast processes add any devices (based on the logs). +It there are no devices added in 5 seconds it will try the next interface. For the automatic IP discovery to work you should have at least one UPnP/Sonos/Chromecast device on your network. + +If the start script is not able to find the right IP automatically you can fix it in `scripts/start-stop-status` by setting your own local IP (of your nas/router) and building your own package. + +Look for the following lines: + +```bash +# If you want to start the airconnect processes on a specific IP please uncomment the following lines +# and set your own local IP address: +# +# start_airconnect_on_ip "1.2.3.4" || true +# return 0 +``` + +Alternatively you can open an issue and include your network interface list and your local IP. + +## Build + +### Run shellcheck (optional) + +```bash +make shellcheck +``` + +### Build packages for all architectures + +```bash +make clean build-all +``` + +### Build a package for a specific architecture + +```bash +ARCH=arm make clean build +``` + +Possible values for **ARCH**: `arm, aarch64, x86, x86-64` + +You can find the built packages in the **dist** directory. + +## Debugging + +If you want to see more logs then change the `-d all=error` parameter in `scripts/start-stop-status` and rebuild the package, then [install it again](#install-via-command-line). + +## License + +See [LICENSE](https://github.com/philippe44/AirConnect/blob/master/LICENSE). diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..1992ff6 --- /dev/null +++ b/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +echo +echo "Build Start" + +rm -r -f dist + +set -euo pipefail + +ARCH_LIST="arm aarch64 x86 x86-64" +MAKE=`which make` + +for arch in ${ARCH_LIST}; do + export ARCH=${arch} + $MAKE clean build +done + +rm -r -f target + +echo +echo "Build complete, you can find the packages under the dist directory" + +echo "how to install new package on synology x86 devices via commandline:" +echo "sudo synopkg install dist/AirConnect-x86-64-XXX.spk" +# sudo synopkg install dist/AirConnect-x86-64-${VERSION}.spk diff --git a/scripts/postinst b/scripts/postinst new file mode 100644 index 0000000..6731b00 --- /dev/null +++ b/scripts/postinst @@ -0,0 +1,15 @@ +#!/bin/sh + +# Ensure user 'airconnect' exists before doing anything + +if ! synouser --get airconnect > /dev/null 2>&1 +then + # create user with random password + echo Creating AirConnect user. + synouser --add airconnect "$(uuidgen | cut -c-8)" 'AirConnect User' 0 '' '' +else + echo Securing existing AirConnect user + synouser --modify airconnect 'AirConnect User' 0 '' +fi + +exit 0 diff --git a/scripts/postuninst b/scripts/postuninst new file mode 100644 index 0000000..c52d3c2 --- /dev/null +++ b/scripts/postuninst @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/scripts/postupgrade b/scripts/postupgrade new file mode 100644 index 0000000..c52d3c2 --- /dev/null +++ b/scripts/postupgrade @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/scripts/preinst b/scripts/preinst new file mode 100644 index 0000000..c52d3c2 --- /dev/null +++ b/scripts/preinst @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/scripts/preuninst b/scripts/preuninst new file mode 100644 index 0000000..c52d3c2 --- /dev/null +++ b/scripts/preuninst @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/scripts/preupgrade b/scripts/preupgrade new file mode 100644 index 0000000..c52d3c2 --- /dev/null +++ b/scripts/preupgrade @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/scripts/start-stop-status b/scripts/start-stop-status new file mode 100644 index 0000000..1f81dc6 --- /dev/null +++ b/scripts/start-stop-status @@ -0,0 +1,127 @@ +#!/bin/sh + +set -eu + +AIRCONNECT_DIR="/var/packages/AirConnect/target" + +CONFIG_UPNP_FILE="/volume1/development/workspace/AirConnect-Synology/dist/config.xml" +CONFIG_CAST_FILE="/volume1/development/workspace/AirConnect-Synology/dist/config-cast.xml" + +logfile="/tmp/airconnect.log" + +start_airconnect_on_ip () +{ + ip="$1" + port=49154 + { + echo "[$(date +'%T')] Starting airupnp on ${ip}:${port}" + } >> ${logfile} + su airconnect -s /bin/sh -c "${AIRCONNECT_DIR}/airupnp -b ${ip}:${port} -x \"${CONFIG_UPNP_FILE}\" -z -f ${logfile}" >> ${logfile} 2>&1 + + + { + echo "[$(date +'%T')] Starting aircast on ${ip}" + } >> ${logfile} + su airconnect -s /bin/sh -c "${AIRCONNECT_DIR}/aircast -b ${ip} -x \"${CONFIG_CAST_FILE}\" -z -f ${logfile}" >> ${logfile} 2>&1 + + cnt=0 + while [ $cnt -lt 5 ]; do + if grep -q "adding renderer" ${logfile}; then + return 0 + fi + cnt=$((cnt + 1)) + sleep 1 + done + return 1 +} + +start_airconnect () +{ + rm -f "${logfile}" + touch "${logfile}" + chown airconnect:users "${logfile}" + + # If you want to start the airconnect processes on a specific IP please uncomment the following lines + # and set your own local IP address: + # + # start_airconnect_on_ip "1.2.3.4" || true + # return 0 + + interfaces=$(ifconfig -a | grep 'Ethernet' -A 1 | grep -E 'inet (addr:)?(192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.)' -B 1 | grep 'Ethernet' | grep -v "lbr0" | grep -v "docker" | cut -d ' ' -f 1) + + if [ -z "$interfaces" ]; then + echo "Failed to get local Ethernet interfaces" > "${SYNOPKG_TEMP_LOGFILE}" + exit 1 + fi + + # Try lbr0 interface first if exists + interfaces="lbr0 $interfaces" + + for interface in $interfaces; do + ip=$(/sbin/ifconfig "$interface" 2> /dev/null | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}') + if [ -n "$ip" ]; then + if start_airconnect_on_ip "$ip"; then + return 0 + else + stop_airconnect + fi + fi + done + + echo "Failed to start AirConnect on any of the local interfaces, please make sure you have at least one UPnP/Sonos/Chromecast device on your network" > "${SYNOPKG_TEMP_LOGFILE}" + exit 1 +} + +stop_airconnect () +{ + killall -16 airupnp + killall -16 aircast +} + +airconnect_status () +{ + # Check if ps has ax options + if ps ax >/dev/null 2>&1; then + # shellcheck disable=SC2009 + if ! ps ax | grep -E 'airupnp|aircast' | grep -v -q grep; then + return 1 + fi + else + # shellcheck disable=SC2009 + if ! ps | grep -E 'airupnp|aircast' | grep -v -q grep; then + return 1 + fi + fi + + return 0 +} + +case $1 in + start) + echo Starting AirConnect ... + start_airconnect + exit $? + ;; + stop) + echo Stopping AirConnect ... + stop_airconnect + exit $? + ;; + status) + if airconnect_status + then + echo AirConnect is running + exit 0 + else + echo AirConnect is not running + exit 1 + fi + ;; + log) + echo "${logfile}" + exit 0 + ;; + *) + exit 1 + ;; +esac