Bliss is a Java application written by Dan Gravell which can manage file names, tags and album art for your music collection and which can also enforce their consistency. It is designed to be left running once installed so that albums you add later will have these same policies applied to them automatically. It supports a wide range of music formats, and effortlessly deals with very large collections – apparently even ones containing quite obscure recordings. My own collection didn’t really put this to the test, since it doesn’t contain bootlegs, live sets or rarities plus I had also already obtained cover art for it (from back when CoverFlow first graced the screen of my iPhone 2G).
I could see from referrals to this blog that people were asking Dan for a Synology package which he didn’t have the time to investigate, and so I thought that it would make an interesting little project, especially since a NAS is the ideal device to run Bliss on. Although there was already a Howto post on the Synology forums, that guide only really covered getting the basic functionality of Bliss up and running – it was missing the best bits: the filesystem watching and audio fingerprinting features. These depend on natively compiled binaries, which Bliss doesn’t include for ARM or PowerPC CPUs. Getting these working provided precisely the sort of challenge I like. Not only were they difficult to compile, but getting them integrated into the various OSGi ‘bundles’ that make up Bliss was quite involved too.
Bliss uses an open source library called Chromaprint, itself part of the wider Acoustid project. The aim is to scan an audio file, to produce a fingerprint of the sound, and then to compare this against an open online database such as MusicBrainz.org to identify music regardless of compression codec used. Its author Lukáš Lalinský explains how it works.
Synology Package Installation
- In Synology DSM’s Package Center, click Settings and add my package repository:
- The repository will push its certificate automatically to the NAS, which is used to validate package integrity. Set the Trust Level to Synology Inc. and trusted publishers:
- Since Bliss is a Java application, you will need to install one of my Java SE Embedded packages first (Java 7 or 8) if you have not already done so. Read the instructions on that page carefully too.
- Now browse the Community section in Package Center to install Bliss:
The repository only displays packages which are compatible with your specific model of NAS. If you don’t see Bliss in the list, then either your NAS model or your DSM version are not supported at this time. DSM 5.0 is the minimum supported version for this package, though you will need DSM 6.0 or later for audio fingerprinting support. - When the Bliss package is running you can manage it using the icon in the main DSM application menu using the button in the top left corner:
Package scripts
For information, here are the package scripts so you can see what it’s going to do. You can get more information about how packages work by reading the Synology 3rd Party Developer Guide.
installer.sh
#!/bin/sh #--------BLISS installer script #--------package maintained at pcloadletter.co.uk DOWNLOAD_URL="`wget -qO- http://www.blisshq.com/app/latest-linux-version`" DOWNLOAD_FILE="`echo ${DOWNLOAD_URL} | sed -r "s%^.*/(.*)%\1%"`" SYNO_CPU_ARCH="`uname -m`" [ "${SYNO_CPU_ARCH}" == "x86_64" ] && [ ${SYNOPKG_DSM_VERSION_MAJOR} -ge 6 ] && SYNO_CPU_ARCH="x64" [ "${SYNO_CPU_ARCH}" == "x86_64" ] && SYNO_CPU_ARCH="i686" [ "${SYNOPKG_DSM_ARCH}" == "armada375" ] && SYNO_CPU_ARCH="armv7l" [ "${SYNOPKG_DSM_ARCH}" == "armada38x" ] && SYNO_CPU_ARCH="armhfneon" [ "${SYNOPKG_DSM_ARCH}" == "comcerto2k" ] && SYNO_CPU_ARCH="armhfneon" [ "${SYNOPKG_DSM_ARCH}" == "alpine" ] && SYNO_CPU_ARCH="armhfneon" [ "${SYNOPKG_DSM_ARCH}" == "alpine4k" ] && SYNO_CPU_ARCH="armhfneon" [ "${SYNOPKG_DSM_ARCH}" == "monaco" ] && SYNO_CPU_ARCH="armhfneon" [ ${SYNOPKG_DSM_VERSION_MAJOR} -ge 6 ] && NATIVE_BINS_URL="http://packages.pcloadletter.co.uk/downloads/bliss-native-${SYNO_CPU_ARCH}.tar.xz" NATIVE_BINS_FILE="`echo ${NATIVE_BINS_URL} | sed -r "s%^.*/(.*)%\1%"`" #'ua' prefix means wget user-agent will be customized INSTALL_FILES="ua${DOWNLOAD_URL} ${NATIVE_BINS_URL}" PID_FILE="${SYNOPKG_PKGDEST}/bliss.pid" TEMP_FOLDER="`find / -maxdepth 2 -path '/volume?/@tmp' | head -n 1`" APP_TEMP="${TEMP_FOLDER}/${SYNOPKG_PKGNAME}" source /etc/profile pre_checks () { if [ -z ${JAVA_HOME} ]; then echo "Java is not installed or not properly configured. JAVA_HOME is not defined. " >> $SYNOPKG_TEMP_LOGFILE echo "Download and install the Java Synology package from http://wp.me/pVshC-z5" >> $SYNOPKG_TEMP_LOGFILE exit 1 fi if [ ! -f ${JAVA_HOME}/bin/java ]; then echo "Java is not installed or not properly configured. The Java binary could not be located. " >> $SYNOPKG_TEMP_LOGFILE echo "Download and install the Java Synology package from http://wp.me/pVshC-z5" >> $SYNOPKG_TEMP_LOGFILE exit 1 fi JAVA_VER=`java -version 2>&1 | sed -r "/^.* version/!d;s/^.* version \"[0-9]\.([0-9]).*$/\1/"` if [ ${JAVA_VER} -lt 7 ]; then echo "This version of Bliss requires Java 7 or newer. Please update your Java package. " >> $SYNOPKG_TEMP_LOGFILE exit 1 fi if [ ${SYNOPKG_DSM_VERSION_MAJOR} -lt 6 ]; then echo "Please note that native binary support for song identification via audio fingerprinting requires DSM 6.0" >> $SYNOPKG_TEMP_LOGFILE fi } preinst () { pre_checks cd ${TEMP_FOLDER} for WGET_URL in ${INSTALL_FILES} do WGET_FILENAME="`echo ${WGET_URL} | sed -r "s%^.*/(.*)%\1%"`" [ -f ${TEMP_FOLDER}/${WGET_FILENAME} ] && rm ${TEMP_FOLDER}/${WGET_FILENAME} #this will allow blisshq.com to track the number of downloads from Synology users WGET_URL=`echo ${WGET_URL} | sed -e "s/^ua/--user-agent=Synology --referer=http:\/\/pcloadletter.co.uk\/2012\/09\/17\/bliss-package-for-synology /"` wget ${WGET_URL} if [[ $? != 0 ]]; then if [ -d ${PUBLIC_FOLDER} ] && [ -f ${PUBLIC_FOLDER}/${WGET_FILENAME} ]; then cp ${PUBLIC_FOLDER}/${WGET_FILENAME} ${TEMP_FOLDER} else echo "There was a problem downloading ${WGET_FILENAME} from the official download link, " >> $SYNOPKG_TEMP_LOGFILE echo "which was \"${WGET_URL}\" " >> $SYNOPKG_TEMP_LOGFILE echo "Alternatively, you may download this file manually and place it in the 'public' shared folder. " >> $SYNOPKG_TEMP_LOGFILE exit 1 fi fi done exit 0 } postinst () { #run the installer cd ${TEMP_FOLDER} echo "INSTALL_PATH=${SYNOPKG_PKGDEST}" > ${TEMP_FOLDER}/bliss-synology.properties java -jar ${TEMP_FOLDER}/${DOWNLOAD_FILE} -options ${TEMP_FOLDER}/bliss-synology.properties > /dev/null && rm ${TEMP_FOLDER}/${DOWNLOAD_FILE} rm ${TEMP_FOLDER}/bliss-synology.properties sed -i "s%^#!/bin/bash%#!/bin/sh%" ${SYNOPKG_PKGDEST}/bin/bliss.sh #stow jar files containing Synology versions of native code if [ -f ${TEMP_FOLDER}/${NATIVE_BINS_FILE} ]; then mkdir ${SYNOPKG_PKGDEST}/syno-native cd ${SYNOPKG_PKGDEST}/syno-native tar xJf ${TEMP_FOLDER}/${NATIVE_BINS_FILE} fi #record the CPU architecture echo "${SYNO_CPU_ARCH}" > syno_cpu_arch.txt #make changes to Bliss launcher script so that pid file is created for Java process sed -r -i "s%^(exec .*)$%\1 > ${SYNOPKG_PKGDEST}/bliss.out 2>\&1 \&%" ${SYNOPKG_PKGDEST}/bin/bliss.sh echo "echo \$! > ${PID_FILE}" >> ${SYNOPKG_PKGDEST}/bin/bliss.sh #set some additional system properties (temp folder, prefs sync interval) EXTRA_OPTS="-Djava.io.tmpdir=${TEMP_FOLDER} -Djava.util.prefs.syncInterval=86400 -Djava.net.preferIPv4Stack=true" sed -r -i "s%-splash:bliss-splash.png%${EXTRA_OPTS}%" ${SYNOPKG_PKGDEST}/bin/bliss.sh sed -r -i "s%-XX:HeapDumpPath=/tmp%-XX:HeapDumpPath=${TEMP_FOLDER}%" ${SYNOPKG_PKGDEST}/bin/bliss.sh #create log file to allow package start errors to be captured [ -e ${SYNOPKG_PKGDEST}/bliss.out ] || touch ${SYNOPKG_PKGDEST}/bliss.out #add firewall config /usr/syno/bin/servicetool --install-configure-file --package /var/packages/${SYNOPKG_PKGNAME}/scripts/${SYNOPKG_PKGNAME}.sc > /dev/null exit 0 } preuninst () { `dirname $0`/stop-start-status stop exit 0 } postuninst () { #clean up temp [ -d ${TEMP_FOLDER}/Bliss ] && rm -rf ${TEMP_FOLDER}/Bliss #remove firewall config if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then /usr/syno/bin/servicetool --remove-configure-file --package ${SYNOPKG_PKGNAME}.sc > /dev/null fi exit 0 } preupgrade () { `dirname $0`/stop-start-status stop pre_checks exit 0 } postupgrade () { exit 0 }
start-stop-status.sh
#!/bin/sh #--------BLISS start-stop-status script #--------package maintained at pcloadletter.co.uk PKG_FOLDER="`dirname $0 | cut -f1-4 -d'/'`" ENGINE_SCRIPT="${PKG_FOLDER}/target/bin/bliss.sh" DNAME="`dirname $0 | cut -f4 -d'/'`" PID_FILE="${PKG_FOLDER}/target/bliss.pid" DLOG="${PKG_FOLDER}/target/bliss.out" SYNO_CPU_ARCH="`cat ${PKG_FOLDER}/target/syno-native/syno_cpu_arch.txt`" source /etc/profile source /root/.profile start_daemon () { #update the package version number in case of an in-app update BLISS_BUNDLE_DIR="`grep -r --include='*.info' com.elsten.bliss.bundle ${PKG_FOLDER}/target/felix-cache/ | cut -f1 -d':'`" BLISS_BUNDLE_DIR="`dirname ${BLISS_BUNDLE_DIR}`" BLISS_VERSION=`grep "version" ${PKG_FOLDER}/INFO | sed -r "s/^.*([0-9]{8}).*$/\1/"` if [ -d ${BLISS_BUNDLE_DIR} ]; then find ${BLISS_BUNDLE_DIR} -name *.jar > /tmp/bliss-v-check.txt while IFS="" read -r FILE_TO_PARSE; do if [ -e ${FILE_TO_PARSE} ]; then #no unzip command in DSM 6.0 if [ -e /usr/bin/7z ]; then DETECTED_VERSION=`7z e -so ${FILE_TO_PARSE} META-INF/MANIFEST.MF 2> /dev/null | grep Bundle-Version | cut -f4 -d'.' | cut -c1-8` else DETECTED_VERSION=`unzip -p ${FILE_TO_PARSE} META-INF/MANIFEST.MF | grep Bundle-Version | cut -f4 -d'.' | cut -c1-8` fi fi if [ ${DETECTED_VERSION} -gt ${BLISS_VERSION} ]; then BLISS_VERSION=${DETECTED_VERSION} fi done < /tmp/bliss-v-check.txt fi rm /tmp/bliss-v-check.txt sed -r -i "s/^version=\"[0-9]{8}/version=\"${BLISS_VERSION}/" /var/packages/Bliss/INFO #update the CPU-specific repository customizations (in case of an in-app update) #catch both armv5te and armv7l if [ "${SYNO_CPU_ARCH}" != "${SYNO_CPU_ARCH/arm/}" ]; then sed -i "s/policy\.tag\.auto\.linux\.x86/policy\.tag\.auto\.linux\.ARM_le/g" ${PKG_FOLDER}/target/bliss-bundle/repository.xml fi if [ "${SYNO_CPU_ARCH}" == "ppc" ]; then sed -i "s/policy\.tag\.auto\.linux\.x86/policy\.tag\.auto\.linux\.PowerPC/g" ${PKG_FOLDER}/target/bliss-bundle/repository.xml fi #overwrite native lib bundles with syno versions (in case of an in-app update) if [ -e ${PKG_FOLDER}/target/syno-native/ ]; then cp ${PKG_FOLDER}/target/syno-native/* ${PKG_FOLDER}/target/bliss-bundle fi cd ${PKG_FOLDER} ${ENGINE_SCRIPT} > /dev/null 2>> ${DLOG} if [ -z ${SYNOPKG_PKGDEST} ]; then #script was manually invoked, need this to show status change in Package Center [ -e ${PKG_FOLDER}/enabled ] || touch ${PKG_FOLDER}/enabled fi } stop_daemon () { echo "Stopping ${DNAME}" >> ${DLOG} kill `cat ${PID_FILE}` wait_for_status 1 20 || kill -9 `cat ${PID_FILE}` rm -f ${PID_FILE} if [ -z ${SYNOPKG_PKGDEST} ]; then #script was manually invoked, need this to show status change in Package Center [ -e ${PKG_FOLDER}/enabled ] && rm ${PKG_FOLDER}/enabled fi } daemon_status () { if [ -f ${PID_FILE} ] && kill -0 `cat ${PID_FILE}` > /dev/null 2>&1; then return fi rm -f ${PID_FILE} return 1 } wait_for_status () { counter=$2 while [ ${counter} -gt 0 ]; do daemon_status [ $? -eq $1 ] && return let counter=counter-1 sleep 1 done return 1 } case $1 in start) if daemon_status; then echo ${DNAME} is already running with PID `cat ${PID_FILE}` exit 0 else echo Starting ${DNAME} ... start_daemon exit $? fi ;; stop) if daemon_status; then echo Stopping ${DNAME} ... stop_daemon exit $? else echo ${DNAME} is not running exit 0 fi ;; restart) stop_daemon start_daemon exit $? ;; status) if daemon_status; then echo ${DNAME} is running with PID `cat ${PID_FILE}` exit 0 else echo ${DNAME} is not running exit 1 fi ;; log) echo "${DLOG}" exit 0 ;; *) echo "Usage: $0 {start|stop|status|restart}" >&2 exit 1 ;; esac
Changelog:
- 20160606-0011 Substantial overhaul for DSM 6.0, incorporating many enhancements developed for other packages, updated to Bliss version 20160606, DSM 6.0 newer is now required for audio track fingerprinting (fpcalc is compiled to depend on ffmpeg 2.7.1), added support for several newer Synology products, improved accuracy of temp folder detection, in-app updating should also be fixed
- 20150522-0010 Substantial re-write (hence the long delay):
Updated to Bliss version 20150522
DSM 5.0 newer is now required (fpcalc is compiled to depend on FFmpeg 2.0.2)
Now that Intel systems running DSM 5.0+ use a newer glibc, replacement Intel binaries are no longer needed
Added support for Mindspeed Comcerto 2000 CPU in DS414j
Added support for Intel Atom C2538 (avoton) CPU in various models
Added support for ppc853x CPU in older PowerPC models
Added support for Marvell Armada 375 CPU in DS215j
Added support for Intel Evansport CPU in DS214Play and DS415Play
Switched to using root account – no more adding account permissions, package upgrades will no longer break this
DSM Firewall application definition added
Tested with DSM Task Scheduler to allow package to start/stop at certain times of day, saving RAM when not needed
Daemon init script now uses a proper PID file instead of the unreliable method of using grep on the output of ps
Daemon init script can be run from the command line
Switched to .tar.xz compression for native binaries to reduce web hosting storage footprint
Improved accuracy of temp folder detection
Package is now signed with repository private key
User Agent customization while downloading Bliss package from blisshq.com to allow download stats gathering - 20130213-0009 Updated to Bliss 20130213, and will correctly report version in Package Center after an in-app update
- 20130131-0008 Updated to Bliss 20130131
- 20121112-0007 Fixes for DSM 4.2
- 20121112-006 Updated to Bliss 20121112
- 20121019-005 Updated to Bliss 20121019
- 20121002-004 Updated to Bliss 20121002
- 20120830-003 Added support for Freescale QorIQ PowerPC CPUs used in some Synology x13 series products, PowerPC processors in previous Synology generations with older glibc versions are not supported
- 20120830-002 Hopefully fixed Java prefs polling issue that prevented NAS hibernation
- 20120830-001 initial public release
Build Notes
Chromaprint uses some complex maths functions that FFmpeg can provide (specifically Fourier Transform), and FFmpeg’s shared libraries are already included with Synology DSM. Building Chromaprint linked to those existing libraries results in a minuscule 78KB build of fpcalc, rather than the statically compiled ones for various OS and CPU architectures included with Bliss, which weigh in at several megabytes each. I think I’m finally ‘getting’ what open source is all about, which is nice since that was my objective in experimenting with my NAS. To prevent fpcalc building and linking to its dynamic library libchromaprint.so and to get it to detect FFmpeg properly I had to carefully inspect the Makefiles to find the correct build syntax:
FFMPEG_DIR=${TOOLCHAIN} cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=NO .
FFMPEG_DIR is the base folder from which CMake will look for lib/libavcodec.so and include/libavcodec/avfft.h.
Once I had got past the obstacle of compiling the native code, I needed to liaise back and forth with Dan to understand how Bliss was dealing with its libraries and how I could replace the built-in versions. Originally this was quite a kludge but Dan has since abstracted out the native binaries into their own OSGI bundle fragments which makes things a lot easier, and allows Bliss to survive in-app updates until those native components are superseded. The Synology package provides the following architecture-specific jar files (with corresponding edits to their manifest) which contain the fpcalc binary from Chromaprint. Thank you to Dan for all the quick answers!
- com.elsten.bliss.policy.tag.auto.linux.x86-1.0.1.jar
- com.elsten.bliss.policy.tag.auto.linux.amd64-1.0.1.jar
- com.elsten.bliss.policy.tag.auto.linux.ARM_le-1.0.1.jar (various versions)
- com.elsten.bliss.policy.tag.auto.linux.PowerPC-1.0.1.jar