diff --git a/QPKG/package_routines b/QPKG/package_routines
index 83eba56..cd84a8d 100755
--- a/QPKG/package_routines
+++ b/QPKG/package_routines
@@ -112,7 +112,7 @@ QPKG_NAME="RoonServer"
PKG_MAIN_REMOVE="{
## Remove Roon Servers docker image
- CS_DIR=`$CMD_GETCFG "container-station" Install_Path -f SYS_QPKG_CONFIG_FILE`
+ CS_DIR=`$CMD_GETCFG "container-station" Install_Path -f "${SYS_QPKG_CONFIG_FILE}"`
$CS_DIR/bin/system-docker image rm 'ghcr.io/roonlabs/roonserver:latest'
}"
@@ -153,7 +153,7 @@ pkg_install(){
## Creating required folders and setting permissions
"${CMD_MKDIR}" -m 777 "${SYS_QPKG_DIR}"/id
- CS_DIR=`$CMD_GETCFG "container-station" Install_Path -f SYS_QPKG_CONFIG_FILE`
+ CS_DIR=`$CMD_GETCFG "container-station" Install_Path -f "${SYS_QPKG_CONFIG_FILE}"`
# Pull Roon Server docker image, so the first qpkg launch will take less time.
$CS_DIR/bin/system-docker pull 'ghcr.io/roonlabs/roonserver:latest'
diff --git a/QPKG/qpkg.cfg b/QPKG/qpkg.cfg
index ca938d3..53e52f4 100644
--- a/QPKG/qpkg.cfg
+++ b/QPKG/qpkg.cfg
@@ -3,7 +3,7 @@ QPKG_NAME="RoonServer"
# Name of the display application.
QPKG_DISPLAY_NAME="Roon Server"
# Version of the packaged application.
-QPKG_VER="2026-04-14"
+QPKG_VER="2026-04-15"
# Author or maintainer of the package
QPKG_AUTHOR="Christopher Rieke"
# License for the packaged application
@@ -12,7 +12,7 @@ QPKG_AUTHOR="Christopher Rieke"
QPKG_SUMMARY="Roon organizes your personal music files, TIDAL streams, and internet radio stations and adds rich data like artist photos, bios, tour dates, lyrics, credits, and more. Using Roon Server with remote apps for Mac, Windows, iOS, and Android you can stream audio around your home to Sonos, AirPlay, Squeezebox, and Roon Ready devices."
# Preferred number in start/stop sequence.
-QPKG_RC_NUM="101"
+QPKG_RC_NUM="200"
# Init-script used to control the start and stop of the installed application.
QPKG_SERVICE_PROGRAM="RoonServer.sh"
diff --git a/QPKG/shared/RoonServer.sh b/QPKG/shared/RoonServer.sh
index 8e243d7..4cbf516 100755
--- a/QPKG/shared/RoonServer.sh
+++ b/QPKG/shared/RoonServer.sh
@@ -4,10 +4,10 @@ QPKG_NAME="RoonServer"
QPKG_ROOT=`/sbin/getcfg $QPKG_NAME Install_Path -f ${CONF}`
QCS_NAME="container-station"
-QCS_QPKG_DIR=$(/sbin/getcfg $QCS_NAME Install_Path -f $CONF)
-DOCKER_CMD=$QCS_QPKG_DIR/bin/system-docker
+QCS_QPKG_DIR=$(/sbin/getcfg $QCS_NAME Install_Path -f ${CONF})
+DOCKER_CMD="${QCS_QPKG_DIR}/bin/system-docker"
CONTAINER_NAME=roonserver
-COMPOSE_YML_DIR=$QPKG_ROOT/docker/compose
+COMPOSE_YML_DIR="${QPKG_ROOT}/docker/compose"
ROONSERVER_OPTIONS=(`/sbin/getcfg $QPKG_NAME options -f ${CONF}`)
ROON_CHANNEL="production"
@@ -34,6 +34,9 @@ ROON_DATABASE_DIR_FREE_INODES=`df -PThi "${ROON_DATAROOT}" | awk '{print $5}' |
ROON_FFMPEG_DIR="${ROON_DATAROOT}/bin"
ROON_LOG_FILE="${ROON_DATAROOT}/RoonOnNAS.log.txt"
+echo $(basename "$0") >> ${ROON_LOG_FILE}
+echo $@ >> ${ROON_LOG_FILE}
+
ST_COLOR="\033[38;5;34m"
HL_COLOR="\033[38;5;197m"
REG_COLOR="\033[0m"
@@ -44,22 +47,6 @@ do
declare $i=true
done
-
-compose_docker_yml_files () {
-COMPOSE_FILES="\
- -f ${COMPOSE_YML_DIR}/roonserver.yml \
- -f ${COMPOSE_YML_DIR}/platform_specific.yml "
-
-[ -z ${smb_cifs+x} ] || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/smb_cifs_support.yml"
-
-( [ -z ${usb_audio+x} ] && [ -z ${hdmi_audio+x} ] ) || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/audio.yml"
-
-[ -z ${usb_audio+x} ] || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/audio_usb.yml"
-
-[ -z ${hdmi_audio+x} ] || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/audio_hdmi.yml"
-}
-
-
## Log Function
echolog () {
TIMESTAMP=$(date +%d.%m.%y-%H:%M:%S)
@@ -77,8 +64,18 @@ echolog () {
fi
}
-info ()
-{
+compose_docker_yml_files () {
+ COMPOSE_FILES="\
+ -f ${COMPOSE_YML_DIR}/roonserver.yml \
+ -f ${COMPOSE_YML_DIR}/platform_specific.yml "
+
+ [ -z ${smb_cifs+x} ] || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/smb_cifs_support.yml"
+ ( [ -z ${usb_audio+x} ] && [ -z ${hdmi_audio+x} ] ) || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/audio.yml"
+ [ -z ${usb_audio+x} ] || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/audio_usb.yml"
+ [ -z ${hdmi_audio+x} ] || COMPOSE_FILES="${COMPOSE_FILES} -f $COMPOSE_YML_DIR/audio_hdmi.yml"
+}
+
+info () {
## Echoing System Info
echolog "ROON_DATABASE_DIR" "${ROON_DATAROOT} - [`[ -d \"${ROON_DATAROOT}\" ] && echo \"available\" || echo \"not available\"`]"
echolog "ROON_DATABASE_DIR_FS" "${ROON_DATABASE_DIR_FS}"
@@ -95,30 +92,42 @@ info ()
echolog "Roon-Channel" "${ROON_CHANNEL}"
}
-RoonOnNAS_folderCheck ()
-{
+RoonOnNAS_folderCheck () {
if [ -d "${ROONONNAS_DIR}" ]; then
[ -d "${ROONONNAS_DIR}/RoonOnNAS" ] || mkdir "${ROONONNAS_DIR}/RoonOnNAS"
[ -d "${ROONONNAS_DIR}/RoonOnNAS/bin" ] || mkdir "${ROONONNAS_DIR}/RoonOnNAS/bin"
fi
}
-start_RoonServer () {
- if [ "${ROON_DATAROOT}" != "/RoonOnNAS" ] && [ -d "${ROON_DATAROOT}" ]; then
- compose_docker_yml_files
- export_vars
-
- ## Creating required directories, if they do not exist
- [ -d "$ROON_ID_HOST_DIR" ] || mkdir "$ROON_ID_HOST_DIR"
- [ -d "$ROON_TMP_DIR" ] || mkdir "$ROON_TMP_DIR"
-
- ${DOCKER_CMD} compose ${COMPOSE_FILES} up -d
-
- fi
+getCSStatus () {
+ echo "$(/sbin/getcfg container-station status -f /etc/qpkg_run_status)"
+}
+checkCS () {
+ case $(getCSStatus) in
+ 0)
+ echolog "Container Station down."
+ exit 1;
+ ;;
+ 1)
+ echolog "Container Station is starting..."
+ SECONDS=0
+ until [[ $(getCSStatus) == "2" ]]; do
+ if (( SECONDS > 120 )); then
+ echolog "Giving up..."
+ exit 1
+ fi
+ echolog "($SECONDS) Container Station is not up yet. Waiting..."
+ sleep 5
+ done
+ ;;
+ 2)
+ echolog "Container Station is up."
+ ;;
+ esac
}
-export_vars ()
-{
+
+export_vars () {
export ROON_DATAROOT
export QPKG_ROOT
export QNAP_MODEL
@@ -128,55 +137,90 @@ export_vars ()
export ROON_CHANNEL
}
-start_daemon ()
-{
+start_webpanel () {
[ -f ${ROON_DATAROOT}/earlyaccess.txt ] && ROON_CHANNEL="earlyaccess"
- info
#Launch the service in the background if RoonServer share exists.
ln -sfn "${QPKG_ROOT}/web" "${WEB_PATH}${WEBUI}"
- start_RoonServer
- }
+}
+
+start_roonserver () {
+ if [ "${ROON_DATAROOT}" != "/RoonOnNAS" ] && [ -d "${ROON_DATAROOT}" ]; then
+ compose_docker_yml_files
+ export_vars
+
+ ## Creating required directories, if they do not exist
+ [ -d "$ROON_ID_HOST_DIR" ] || mkdir "$ROON_ID_HOST_DIR"
+ [ -d "$ROON_TMP_DIR" ] || mkdir "$ROON_TMP_DIR"
+
+ echo "Docker Command: $(ls -lha ${DOCKER_CMD})" 2>&1 >> ${ROON_LOG_FILE}
+ echo "Docker CMD: $(${DOCKER_CMD} --version)" 2>&1 >> ${ROON_LOG_FILE}
+ echo "COMPOSE_FILES: ${COMPOSE_FILES}" 2>&1 >> ${ROON_LOG_FILE}
+ echo "CS Run-Status: $(/sbin/getcfg container-station status -f /etc/qpkg_run_status)" >> ${ROON_LOG_FILE}
+ sleep 60
+ ${DOCKER_CMD} compose ${COMPOSE_FILES} up -d 2>&1 >> ${ROON_LOG_FILE}
+
+ ${DOCKER_CMD} compose ${COMPOSE_FILES} up -d
+ fi
+}
case "$1" in
start)
- ENABLED=$(/sbin/getcfg $QPKG_NAME Enable -u -d FALSE -f $CONF)
- RoonOnNAS_folderCheck
- if [ "$ENABLED" != "TRUE" ]; then
- echolog "$QPKG_NAME is disabled."
- exit 1
- fi
- CONTAINER_ID=$(${DOCKER_CMD} ps -a -q -f name=$CONTAINER_NAME)
- if [ ! "$CONTAINER_ID" ]; then
- echo "not running"
- start_daemon
- else
- echolog "${QPKG_NAME} is already running (ID: $CONTAINER_ID)"
- fi
+ RS_ENABLED=$(/sbin/getcfg $QPKG_NAME Enable -u -d FALSE -f $CONF)
+ CS_ENABLED=$(/sbin/getcfg "container-station" Enable -u -d FALSE -f $CONF)
+
+ if [ "$RS_ENABLED" != "TRUE" ]; then
+ echolog "$QPKG_NAME is disabled."
+ exit 1
+ fi
+ if [ "$CS_ENABLED" != "TRUE" ]; then
+ echolog "Container Station is disabled."
+ exit 1
+ fi
+
+ CONTAINER_ID=$(${DOCKER_CMD} ps -a -q -f name=$CONTAINER_NAME)
+ if [ ! "$CONTAINER_ID" ]; then
+ echolog "Starting Roon Server..."
+ info
+ checkCS
+ RoonOnNAS_folderCheck
+ start_webpanel
+ start_roonserver
+ else
+ echolog "${QPKG_NAME} is already running (ID: $CONTAINER_ID)"
+ fi
;;
stop)
- CONTAINER_ID=$(${DOCKER_CMD} ps -a -q -f name=$CONTAINER_NAME)
- if [ ! "$CONTAINER_ID" ]; then
+ CS_ENABLED=$(/sbin/getcfg "container-station" Enable -u -d FALSE -f $CONF)
+
+ # Check if CS has not been stopped before Roon Server
+ if [ "$CS_ENABLED" == "TRUE" ]; then
+ # --> CS is still up.
+ CONTAINER_ID=$(${DOCKER_CMD} ps -a -q -f name=$CONTAINER_NAME)
+ if [ ! "$CONTAINER_ID" ]; then
+ # --> No roonserver conatiner running
echolog "${QPKG_NAME} is not running."
- else
+ else
+ # --> Stopping roonserver conatiner
echolog "Stopping RoonServer..."
compose_docker_yml_files
export_vars
${DOCKER_CMD} compose ${COMPOSE_FILES} down
- if [[ $2 != "keepwebalive" ]]; then
- rm -rf "${QPKG_ROOT}/web/tmp"/*
- rm "${WEB_PATH}${WEBUI}"
- fi
+ [ -d "${WEB_PATH}${WEBUI}" ] && rm "${WEB_PATH}${WEBUI}"
echolog "RoonServer has been stopped."
+ fi
+ else
+ # -> CS is disabled:
+ # Edge case: CS has been stopped before RoonServer. We can assume RoonServer docker is down. Only the web-panel needs to be removed
+ [ -d "${WEB_PATH}${WEBUI}" ] && rm "${WEB_PATH}${WEBUI}"
+ echolog "RoonServer is not running."
fi
;;
restart)
- isRestart=true
$0 stop
$0 start
;;
-
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
diff --git a/QPKG/shared/web/content/about.php b/QPKG/shared/web/content/about.php
index 77d01ed..c11af6b 100644
--- a/QPKG/shared/web/content/about.php
+++ b/QPKG/shared/web/content/about.php
@@ -90,7 +90,7 @@ $ContributorsManual = array(
-
: 1 ) {
- echo '' . localize("OVERVIEW_ROONSERVER_PANEL_STATUS_RUNNING") . '';
+ echo '' . localize("OVERVIEW_ROONSERVER_PANEL_STATUS_RUNNING") . '';
} else {
echo '' . localize("OVERVIEW_ROONSERVER_PANEL_STATUS_STOPPED") . '';
} ?>
@@ -49,7 +49,7 @@ include_once("/home/httpd/cgi-bin/qpkg/RoonServer/__functions.php");
: