git: 4f656e2c7818 - main - audio/supysonic: New port: Python implementation of the Subsonic server API

From: Jose Alonso Cardenas Marquez <acm_at_FreeBSD.org>
Date: Thu, 24 Oct 2024 00:13:48 UTC
The branch main has been updated by acm:

URL: https://cgit.FreeBSD.org/ports/commit/?id=4f656e2c781805f2693e3580fec60146aac07849

commit 4f656e2c781805f2693e3580fec60146aac07849
Author:     Jesús Daniel Colmenares Oviedo <DtxdF@disroot.org>
AuthorDate: 2024-10-24 00:12:51 +0000
Commit:     Jose Alonso Cardenas Marquez <acm@FreeBSD.org>
CommitDate: 2024-10-24 00:12:51 +0000

    audio/supysonic: New port: Python implementation of the Subsonic server API
    
    Supysonic is a Python implementation of the Subsonic server API.
    
    Current supported features are:
     * browsing (by folders or tags)
     * streaming of various audio files formats
     * transcoding
     * user or random playlists
     * cover art
     * starred tracks/albums and ratings
     * lastfm scrobbling
     * Jukebox mode
    
    WWW: https://supysonic.readthedocs.io/
    
    PR:             270751
---
 GIDs                                               |   2 +-
 UIDs                                               |   2 +-
 audio/Makefile                                     |   1 +
 audio/py-supysonic/Makefile                        |  67 ++++++++++++++
 audio/py-supysonic/distinfo                        |   3 +
 audio/py-supysonic/files/patch-supysonic_config.py |  11 +++
 audio/py-supysonic/files/supysonic-daemon.in       |  43 +++++++++
 audio/py-supysonic/files/supysonic.conf            | 101 +++++++++++++++++++++
 audio/py-supysonic/files/supysonic.in              |  31 +++++++
 audio/py-supysonic/pkg-descr                       |  11 +++
 audio/py-supysonic/pkg-plist                       |   9 ++
 11 files changed, 279 insertions(+), 2 deletions(-)

diff --git a/GIDs b/GIDs
index 00127f18d247..11a30a83f8d9 100644
--- a/GIDs
+++ b/GIDs
@@ -692,7 +692,7 @@ c-lightning:*:735:
 # free: 748
 # free: 749
 # free: 750
-# free: 751
+supysonic:*:751:
 # free: 752
 # free: 753
 # free: 754
diff --git a/UIDs b/UIDs
index 78233d695385..18cad1c4c511 100644
--- a/UIDs
+++ b/UIDs
@@ -697,7 +697,7 @@ c-lightning:*:735:735::0:0:c-lightning Daemon:/var/db/c-lightning:/usr/sbin/nolo
 # free: 748
 # free: 749
 # free: 750
-# free: 751
+supysonic:*:751:751::0:0:Subsonic server API:/var/db/supysonic:/usr/sbin/nologin
 # free: 752
 # free: 753
 # free: 754
diff --git a/audio/Makefile b/audio/Makefile
index 45637fa24cde..1db5025d3a5e 100644
--- a/audio/Makefile
+++ b/audio/Makefile
@@ -672,6 +672,7 @@
     SUBDIR += py-soxr
     SUBDIR += py-speechrecognition
     SUBDIR += py-spotipy
+    SUBDIR += py-supysonic
     SUBDIR += py-torchaudio
     SUBDIR += py-vosk
     SUBDIR += py-wavio
diff --git a/audio/py-supysonic/Makefile b/audio/py-supysonic/Makefile
new file mode 100644
index 000000000000..92907856f994
--- /dev/null
+++ b/audio/py-supysonic/Makefile
@@ -0,0 +1,67 @@
+PORTNAME=	supysonic
+DISTVERSION=	0.7.8
+CATEGORIES=	audio python
+MASTER_SITES=	PYPI
+PKGNAMEPREFIX=	${PYTHON_PKGNAMEPREFIX}
+
+MAINTAINER=	DtxdF@disroot.org
+COMMENT=	Python implementation of the Subsonic server API
+WWW=		https://supysonic.readthedocs.io/
+
+LICENSE=	AGPLv3
+LICENSE_FILE=	${WRKSRC}/LICENSE
+
+RUN_DEPENDS=	${PYTHON_PKGNAMEPREFIX}click>0:devel/py-click@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}flask>0:www/py-flask@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}mediafile>0:devel/py-mediafile@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}peewee>0:databases/py-peewee@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}pillow>0:graphics/py-pillow@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}requests>0:www/py-requests@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}watchdog>0:devel/py-watchdog@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}zipstream-ng>0:archivers/py-zipstream-ng@${PY_FLAVOR}
+
+USES=		python
+USE_PYTHON=	autoplist distutils
+
+USE_RC_SUBR=	supysonic \
+		supysonic-daemon
+
+SUB_LIST=	PYTHON_CMD=${PYTHON_CMD} \
+		USER=supysonic \
+		GROUP=supysonic
+
+USERS=		supysonic
+GROUPS=		supysonic
+
+PLIST_SUB=	GROUP=supysonic \
+		USER=supysonic
+
+OPTIONS_DEFINE=		PGSQL SQLITE3
+OPTIONS_DEFAULT=	GUNICORN SQLITE3 PGSQL MYSQLCLIENT
+OPTIONS_MULTI=		WSGI
+OPTIONS_MULTI_WSGI=	GEVENT GUNICORN WAITRESS
+OPTIONS_RADIO=		MYSQL
+OPTIONS_RADIO_MYSQL=	MYSQLCLIENT PYMYSQL
+
+GEVENT_DESC=		Install with gevent
+GUNICORN_DESC=		Install with gunicorn
+MYSQLCLIENT_DESC=	Install with MySQLdb
+PYMYSQL_DESC=		Install with pymysql
+WAITRESS_DESC=		Install with waitress
+
+GEVENT_RUN_DEPENDS=		${PYTHON_PKGNAMEPREFIX}gevent>0:devel/py-gevent
+GUNICORN_RUN_DEPENDS=		${PYTHON_PKGNAMEPREFIX}gunicorn>0:www/py-gunicorn
+MYSQLCLIENT_RUN_DEPENDS=	${PYTHON_PKGNAMEPREFIX}mysqlclient>0:databases/py-mysqlclient@${PY_FLAVOR}
+PGSQL_RUN_DEPENDS=		${PYTHON_PKGNAMEPREFIX}psycopg2>0:databases/py-psycopg2@${PY_FLAVOR}
+PYMYSQL_RUN_DEPENDS=		${PYTHON_PKGNAMEPREFIX}pymysql>0:databases/py-pymysql@${PY_FLAVOR}
+SQLITE3_RUN_DEPENDS=		${PYTHON_PKGNAMEPREFIX}sqlite3>0:databases/py-sqlite3@${PY_FLAVOR}
+WAITRESS_RUN_DEPENDS=		${PYTHON_PKGNAMEPREFIX}waitress>0:www/py-waitress
+
+post-install:
+	@${MKDIR} ${STAGEDIR}${ETCDIR}
+	${INSTALL_DATA} ${FILESDIR}/supysonic.conf ${STAGEDIR}${ETCDIR}/supysonic.conf.sample
+.for dir in cache log run
+	@${MKDIR} ${STAGEDIR}/var/${dir}/supysonic
+.endfor
+
+.include <bsd.port.mk>
diff --git a/audio/py-supysonic/distinfo b/audio/py-supysonic/distinfo
new file mode 100644
index 000000000000..fbe067806db5
--- /dev/null
+++ b/audio/py-supysonic/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1728951393
+SHA256 (supysonic-0.7.8.tar.gz) = 5a3fb511cf27fd24aaa2a8161dbd87d54369f38b1ea9de449ea19a3a6a51661f
+SIZE (supysonic-0.7.8.tar.gz) = 342988
diff --git a/audio/py-supysonic/files/patch-supysonic_config.py b/audio/py-supysonic/files/patch-supysonic_config.py
new file mode 100644
index 000000000000..7e6f60f7ea33
--- /dev/null
+++ b/audio/py-supysonic/files/patch-supysonic_config.py
@@ -0,0 +1,11 @@
+--- supysonic/config.py.orig	2023-04-08 00:33:44 UTC
++++ supysonic/config.py
+@@ -61,7 +61,7 @@ class DefaultConfig:
+ 
+ class IniConfig(DefaultConfig):
+     common_paths = [
+-        "/etc/supysonic",
++        os.path.join(sys.prefix, "etc/supysonic/supysonic.conf"),
+         os.path.expanduser("~/.supysonic"),
+         os.path.expanduser("~/.config/supysonic/supysonic.conf"),
+         "supysonic.conf",
diff --git a/audio/py-supysonic/files/supysonic-daemon.in b/audio/py-supysonic/files/supysonic-daemon.in
new file mode 100644
index 000000000000..242362d9ab10
--- /dev/null
+++ b/audio/py-supysonic/files/supysonic-daemon.in
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# PROVIDE: supysonic_daemon
+# REQUIRE: NETWORKING LOGIN
+# KEYWORD: shutdown
+#
+# Configuration settings for supysonic-daemon in /etc/rc.conf
+#
+# supysonic_daemon_enable (bool):    Enable supysonic-daemon. (default=NO)
+# supysonic_daemon_user (str):       User to run supysonic-daemon. (default=%%USER%%)
+# supysonic_daemon_log (str):        Send stdout/stderr to a file. (default=/dev/null)
+# supysonic_daemon_flags (str):      Flags used for supysonic-daemon. (default=)
+#
+
+. /etc/rc.subr
+
+name=supysonic_daemon
+rcvar=supysonic_daemon_enable
+
+load_rc_config $name
+
+: ${supysonic_daemon_enable:=NO}
+: ${supysonic_daemon_user:=%%USER%%}
+: ${supysonic_daemon_log:=/dev/null}
+
+pidfile=/var/run/supysonic/daemon.pid
+procname="%%PREFIX%%/bin/supysonic-daemon"
+command_interpreter="%%PYTHON_CMD%%"
+start_cmd="supysonic_daemon_start"
+
+supysonic_daemon_start()
+{
+	echo "Starting supysonic-daemon."
+	/usr/sbin/daemon -c \
+		-p "${pidfile}" \
+		-o "${supysonic_daemon_log}" \
+		-u "${supysonic_daemon_user}" \
+		"${command_interpreter}" \
+		"${procname}" \
+		${supysonic_daemon_flags}
+}
+
+run_rc_command "$1"
diff --git a/audio/py-supysonic/files/supysonic.conf b/audio/py-supysonic/files/supysonic.conf
new file mode 100644
index 000000000000..a7633c20a744
--- /dev/null
+++ b/audio/py-supysonic/files/supysonic.conf
@@ -0,0 +1,101 @@
+[base]
+; A database URI. See the 'schema' folder for schema creation scripts. Note that
+; you don't have to run these scripts yourself.
+; Default: sqlite:////tmp/supysonic/supysonic.db
+database_uri = sqlite:////var/db/supysonic/supysonic.db
+;database_uri = mysql://supysonic:supysonic@localhost/supysonic
+;database_uri = postgres://supysonic:supysonic@localhost/supysonic
+
+; Optional, restrict scanner to these extensions. Default: none
+;scanner_extensions = mp3 ogg
+
+; Should the scanner follow symbolic links? Default: no
+follow_symlinks = no
+
+[webapp]
+; Optional cache directory. Default: /tmp/supysonic
+cache_dir = /var/cache/supysonic
+
+; Main cache max size in MB. Default: 512
+cache_size = 512
+
+; Transcode cache max size in MB. Default: 1024 (1GB)
+transcode_cache_size = 1024
+
+; Optional rotating log file. Default: none
+log_file = /var/log/supysonic/supysonic.log
+
+; Log level. Possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL.
+; Default: WARNING
+log_level = WARNING
+
+; Enable log rotation. Default: yes
+log_rotate = yes
+
+; Enable the Subsonic REST API. You'll most likely want to keep this on, here
+; for testing purposes. Default: on
+;mount_api = on
+
+; Enable the administrative web interface. Default: on
+;mount_webui = on
+
+; Space separated list of prefixes that should be ignored on index endpoints
+; Default: El La Le Las Les Los The
+index_ignored_prefixes = El La Le Las Les Los The
+
+; Enable the ChartLyrics API. Default: off
+online_lyrics = off
+
+[daemon]
+; Socket file the daemon will listen on for incoming management commands
+; Default: /tmp/supysonic/supysonic.sock
+socket = /var/run/supysonic/supysonic.sock
+
+; Defines if the file watcher should be started. Default: yes
+run_watcher = yes
+
+; Delay in seconds before triggering scanning operation after a change have been
+; detected.
+; This prevents running too many scans when multiple changes are detected for a
+; single file over a short time span. Default: 5
+wait_delay = 5
+
+; Command used by the jukebox
+jukebox_command = mplayer -ss %offset %path
+
+; Optional rotating log file for the scanner daemon. Logs to stderr if empty
+log_file = /var/log/supysonic/supysonic-daemon.log
+log_level = INFO
+log_rotate = yes
+
+[lastfm]
+; API and secret key to enable scrobbling. http://www.last.fm/api/accounts
+; Defaults: none
+;api_key =
+;secret =
+
+[listenbrainz]
+; root URL of the ListenBrainz API.
+; Defaults: https://api.listenbrainz.org/
+;api_url =
+
+[transcoding]
+; Programs used to convert from one format/bitrate to another. Defaults: none
+transcoder_mp3_mp3 = lame --quiet --mp3input -b %outrate %srcpath -
+transcoder = ffmpeg -i %srcpath -ab %outratek -v 0 -f %outfmt -
+decoder_mp3 = mpg123 --quiet -w - %srcpath
+decoder_ogg = oggdec -o %srcpath
+decoder_flac = flac -d -c -s %srcpath
+encoder_mp3 = lame --quiet -b %outrate - -
+encoder_ogg = oggenc2 -Q -M %outrate -
+
+; Default format, used when a client requests a bitrate lower than the original
+; file and no specific format
+default_transcode_target = mp3
+
+[mimetypes]
+; Extension to mimetype mappings in case your system has some trouble guessing
+; Default: none
+;mp3 = audio/mpeg
+;ogg = audio/vorbis
+
diff --git a/audio/py-supysonic/files/supysonic.in b/audio/py-supysonic/files/supysonic.in
new file mode 100644
index 000000000000..70b41873fe6d
--- /dev/null
+++ b/audio/py-supysonic/files/supysonic.in
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# PROVIDE: supysonic
+# REQUIRE: NETWORKING LOGIN
+# KEYWORD: shutdown
+#
+# Configuration settings for supysonic in /etc/rc.conf
+#
+# supysonic_enable (bool):    Enable supysonic. (default=NO)
+# supysonic_user (str):       User to run supysonic-server. (default=%%USER%%)
+# supysonic_log (str):        Send stdout/stderr to a file. (default=/dev/null)
+# supysonic_flags (str):      Flags used for supysonic-server. (default=)
+#
+
+. /etc/rc.subr
+
+name=supysonic
+rcvar=supysonic_enable
+
+load_rc_config $name
+
+: ${supysonic_enable:=NO}
+: ${supysonic_user:=%%USER%%}
+: ${supysonic_log:=/dev/null}
+
+pidfile=/var/run/supysonic/server.pid
+procname="%%PYTHON_CMD%%"
+command="/usr/sbin/daemon"
+command_args="-c -p ${pidfile} -o ${supysonic_log} ${procname} %%PREFIX%%/bin/supysonic-server ${supysonic_flags}"
+
+run_rc_command "$1"
diff --git a/audio/py-supysonic/pkg-descr b/audio/py-supysonic/pkg-descr
new file mode 100644
index 000000000000..907d8a16d5c9
--- /dev/null
+++ b/audio/py-supysonic/pkg-descr
@@ -0,0 +1,11 @@
+Supysonic is a Python implementation of the Subsonic server API.
+
+Current supported features are:
+ * browsing (by folders or tags)
+ * streaming of various audio files formats
+ * transcoding
+ * user or random playlists
+ * cover art
+ * starred tracks/albums and ratings
+ * lastfm scrobbling
+ * Jukebox mode
diff --git a/audio/py-supysonic/pkg-plist b/audio/py-supysonic/pkg-plist
new file mode 100644
index 000000000000..8a0da389d785
--- /dev/null
+++ b/audio/py-supysonic/pkg-plist
@@ -0,0 +1,9 @@
+@dir %%ETCDIR%%
+@sample %%ETCDIR%%/supysonic.conf.sample
+@owner %%USER%%
+@group %%GROUP%%
+@dir /var/cache/supysonic
+@dir /var/log/supysonic
+@dir /var/run/supysonic
+@owner
+@group