Go to file
ben 739f2983c0
ben/build-owntone/pipeline/head This commit looks good Details
git fetch --tags
2024-02-12 03:57:40 +01:00
.pipeline Fleshing out readme, and renaming some things 2023-10-12 13:45:26 +02:00
ansible Fleshing out readme, and renaming some things 2023-10-12 13:45:26 +02:00
dark-reader build web ui 2023-09-30 16:13:51 +02:00
dist first commit 2023-09-28 23:26:23 +02:00
docker show file structure of owntone-server 2023-11-12 18:36:04 +01:00
etc Fleshing out readme, and renaming some things 2023-10-12 13:45:26 +02:00
img Fleshing out readme, and renaming some things 2023-10-12 13:45:26 +02:00
target build web ui 2023-09-30 16:13:51 +02:00
.dockerignore first commit 2023-09-28 23:26:23 +02:00
.gitignore git fetch --tags 2024-02-12 03:57:40 +01:00
Dockerfile Fleshing out readme, and renaming some things 2023-10-12 13:45:26 +02:00
Jenkinsfile git fetch --tags 2024-02-12 03:57:40 +01:00
LICENSE Initial commit 2023-09-28 15:36:42 +00:00
README.md Fleshing out readme, and renaming some things 2023-10-12 13:45:26 +02:00
build.sh Fleshing out readme, and renaming some things 2023-10-12 13:45:26 +02:00



Build Status version git github matrix

A custom build of the latest stable version of OwnTone (formerly forked-daapd).

owntone logo

This projects builds, compiles and packages OwnTone, publishing both .deb packages and docker containers of the latest stable version.

Changes to OwnTone

This build includes some (minor) custom changes to Owntone

  • Attempting to use a dark theme (css was generated by the Dark Reader extension for Firefox).
  • Builds the web interface from web-src sources.
    • Defaults to building the web interface, but can be disabled.
    • Builds also contain the pre-compiled web interface in htdocs
    • Builds web interface without minimized javascript
    • Web socket is access by the web interface on /ws (requires reverse proxy) instead of separate port (see web-change-ws-url.sh)
  • Example nginx vhost config file is included (see 01-owntone.conf).

TODO changes:

  • Change the default action to add an item to the queue instead of replacing the queue (see notes below). On a phone or touchscreen it is not so easy to tap the hamburger menu and I often accidentally wipe out my current queue.

Not a fork

I don't want to have to maintain a whole fork of OwnTone, and these are all relatively small and simple, so they are just applied on build-time with shell scripts.

Installing and usage

Config and example files:

Installing .deb with apt

You can either user the gitea repo from git.sudo.is or apt.sudo.is repo.

For git.sudo.is:

sudo curl https://git.sudo.is/api/packages/ben/debian/repository.key -o /etc/apt/keyrings/git.sudo.is-ben.asc
echo "deb [signed-by=/etc/apt/keyrings/git.sudo.is-ben.asc] https://git.sudo.is/api/packages/ben/debian $distribution main" | sudo tee -a /etc/apt/sources.list.d/git.sudo.is-ben.list
sudo apt update
sudo apt install owntone-server

For apt.sudo.is:

sudo curl https://apt.sudo.is/KEY.gpg -o /etc/apt/keyrings/apt.sudo.is.gpg
echo "deb [signed-by=/etc/apt/keyrings/apt.sudo.is.gpg] https://apt.sudo.is /" | sudo tee -a /etc/apt/sources.list.d/apt.sudo.is.list
sudo apt update
sudo apt install owntone-server

Generally the git.sudo.is mirror should probably be preferred.

Docker container

For Ansible users, an example docker_container task is in owntone-docker.yml.

The docker images are based on the ubuntu:latest base image, because of the complex dependencies, and I was not able to figure out how to compile a static binary. I tried following the github pipeline in owntone/owntone-apt but opted for using fpm to build a simple .deb package. Owntone is compiled and packaged in the builder stage, and then installed in the final stage.

To run as a non-root user, you need to use avahi-daemon and dbus from the host.

To do that you can either run the container as --privileged (wrong), or set --security-opt apparmor=unconfined to circumvent AppArmor (better). In both cases, the process acessing /var/run/avahi-daemon/socket needs to have the same UID as owns the socket.

docker run \
    -it \
    --name owntone \
    --net=host \
    --security-opt apparmor=unconfined \
    --user ${uid}:${gid} \
    -v /var/run/dbus:/var/run/dbus \
    -v /run/avahi-daemon/socket:/run/avahi-daemon/socket \
    -v /usr/local/etc/owntone.conf:/etc/owntone.conf \
    -v /var/lg/owntone.log:/var/log/owntone.log \
    -v /var/cache/owntone/:/var/cache/owntone \
    -v /srv/audio/:/srv/audio \

The container has the build args OWNTONE_UID and OWNTONE_GID if you want to build it with a different default user for running OwnTone. Note that you can change the defaults paths /srv/audio and /var/cache/owntone in owntone.conf to better suit your needs, if you want.

The container can also be started as root, and then the owntone binary setuids itself down to the user that you have specified in owntone.conf. This is the default and expected behaviour of OwnTone.

docker run \
    -it \
    --name owntone \
    --net=host \
    --user 0 \
    -v /usr/local/etc/owntone.conf:/etc/owntone.conf \
    -v /var/log/owntone.log:/var/log/owntone.log \
    -v /var/cache/owntone/:/var/cache/owntone \
    -v /srv/audio/:/srv/audio \

When the container is started as root, neither the dbus nor avahi-daemon sockets need to be mounted, because the container will start its own before launching OwnTone.

Note that this username (cant specify uid) has to exist in the container (so if you want to change it you can rebuild the container with different UID and GID build args, use some other UID that already exists in the containers /etc/passwd or create a new user in a custom entrypoint.sh script.)

Differences from upstream OwnTone

Websocket accessed on /ws via reverse proxy

By default OwnTone will spawn a websocket server on port thats separate from the main port (used for the API and webinterface), because it is not expecting to be run behind a reverse proxy. But if you do have a reverse proxy in front of OwnTone (like me), its generally nicer to not have to deal with nonstandard ports and manage extra certificates for them.

Since this isnt a fork, and we're keeping the changeset to a minimum, how OwnTone's websocket server listens hasn't actually been changed. Instead, we've just changed what address the web interface uses to connect to the websocket, and then we let the reverse proxy take care of proxying that to OwnTone's websocket server.

This is the change to the web interface:

diff --git a/web-src/src/App.vue b/web-src/src/App.vue
index 0636fa10..1f6b0bc7 100644
--- a/web-src/src/App.vue
+++ b/web-src/src/App.vue
@@ -168,6 +168,7 @@ export default {

+      wsUrl = protocol + window.location.hostname + '/ws'
       const socket = new ReconnectingWebSocket(wsUrl, 'notify', {
         reconnectInterval: 1000,
         maxReconnectInterval: 2000

We just re-define the wsUrl variable, right before it gets used (for the one and only time). Simple.

The settings websocket_interface and websocket_port port are still used by OwnTone, amd so are port and bind_address for the API. In fact, if you GET /api/config the response still uses the websocket_port value:

local:~$ curl -i https://${owntone_url}/api/config | jq .
  "websocket_port": 3688,
  // ...

Normally, the web interface reads this and uses the websocket_port in the response for to construct the URL that it tries to connect to the websocket server on. It still reads this config, it just doesn't end up using websocket_port because we have re-defined it.

Since the config options are still used by OwnTone, we just need to configure OwnTone to bind both the API server and websocket server to localhost in owntone.conf. This build inclues a default owntone.conf file that uses the default ports but binds both servers to localhost:

general {
    # TCP port to listen on. Default port is 3689 (daap)
    port = 3689
    bind_address = ""          # default: ""

    websocket_port = 3688
    websocket_interface = ""   # default: ""

Since both are now only listening on localhost, we need a reverse proxy to proxy the traffic to OwnTone. These builds include a config for Nginx, but you can of course use any old HTTP(S) reverse proxy. Note that due to some idiosyncrasies of the DAAP protocol, most clients expect to talk to OwnTone in HTTP/1.1. Some clients work fine with HTTP/2 (for example Apple Remote) but others won't work and give you somewhat confusing errors about mismatching versions (implying that it is about the version of OwnTone or the DAAP protocol).

An example vhost config file 01-owntone.conf for Nginx is provided.

If you use Nginx, it is a little bit peculiar about how HTTP versions in the listen directive are set. All virtual hosts with the same address:port par will use the same http version, though the nginx documentation for the ngx_http_core_module makes no mention of this:

The http2 parameter (1.9.5) configures the port to accept HTTP/2 connections. Normally, for this to work the ssl parameter should be specified as well, but nginx can also be configured to accept HTTP/2 connections without SSL.

It's not possible to configure different virtual hosts (on the same address:port) to serve different HTTP versions, Nginx will always use the HTTP version set in the first virtual host that it finds, and ignore the setting for all other virtual hosts. If you try to specify a "narrower" address:port pair than other vhsots, then your OwnTone vhost will become the default for that pair, and start responding for other vhosts as well. If this is a limitation that you have to work around (and you want to keep using nginx) then the best option is to assign a second IP address to the host, and not bind any other nginx vhosts (that use HTTP/2) to it.

Partial filescans from owntone-server#1179

There is an open PR owntone-server#1179 from :github: whatdoineed2do that adds support for partial library file scans. This branch/fork also includes some rather neat UI improvements to the web interface.

This featue is very useful if your library is on a network mount (no inotify) and you for example liksten to mostly podcasts and want your library/queue stay up to date.

This build doesnt include this change by default, but you can enable it if you build it yourself.

curl -X PUT "https://${owntone_url}/api/update?kind=files&path=${scan_path}"

Will start a partial library recan, scanning only $scan_path without rescanning the rest of the library.

Work in progress and future changes

Dark Reader theme

This is a work in progress to build the web interface with the dark theme generated by the Dark Reader Firefox extension.

As I lack moden HTML/CSS skills and havent gotten it to work yet (help welcome).

These are the relevant files:

  • dark-reader.css: CSS exported from the Dark Reader Firefox extension
  • index.html: A copy of index.html the includes <link> tag to include dark-reader.css
  • web-add-dark-reader.sh: Script that gets invoked during the build if OWNTONE_WEB_DARK_READWER is "true".

Some references:

UI improvements

When clicking an item in the UI, the default is replace the current queue with whatever it was that you clicked.

The is slightly annoying, especially when using a phone or touch-interface to navigate OwnTone. I find that trying to hit the three-dots hamburger menu often results in accidentally selecting the item, and replacing the queue.

A better default would be to "Add to queue" or evben "Add next", instead of replacing the whole queue.

I think this happens in or around /src/webapi/index.js#L92, or at least that should provide enough clues to find it in the code.

When an item is clicked in the web interface, a POST request is sent, for example: /api/queue/items/add?uris=library:track:40397&shuffle=false&clear=true&playback=start.

Breaking the parameters down into more-readable JSON:

    "uris": "library:track:1234",
    "shuffle": false,
    "clear": true,
    "playback": start

Basically, we would want to send a request with ?clear=false instead.

Maintaining more complex changes without forking

To manage more complex changes without needing a fork, you can use git diffs as .patch files.

Create the patch file:

cd owntone-server
git diff --no-prefix --patch > ../${name}.patch

Then apply the patch file:

patch -p1 -t -i ../${name}.patch

# or by using git:
git apply ../${name}.patch


To forward mdns packets from the host network to the contaner:

mdns-repeater eth1 docker0