GithubHelp home page GithubHelp logo

izderadicka / audioserve Goto Github PK

View Code? Open in Web Editor NEW
674.0 11.0 34.0 8.43 MB

Simple personal server to serve audiofiles files from folders. Intended primarily for audio books, but anything with decent folder structure will do.

Rust 98.54% Dockerfile 0.15% Shell 0.26% HTML 0.21% C 0.01% Python 0.83%
audio audiobooks steaming

audioserve's Introduction

Audioserve

Build Docker Pulls

[DEMO AVAILABLE - shared secret: mypass]

Simple personal server to serve audio files from directories. Intended primarily for audio books, but anything with decent directories structure will do. Focus here is on simplicity and minimalist design.

Server is written in Rust, new web PWA client (Typescript and Svelte) is focused on modern browsers and is using rather recent functionality (Service Worker), older, less demanding web client is still around - check Web client chapter for details. There is also old Android client, and simple API for custom clients.

For some (now bit outdated) background and video demo check this article(bit old but gives main motivation behind it) Audioserve Audiobooks Server - Stupidly Simple or Simply Stupid?

If you will install audioserve and make it available on Internet do not underestimate security.

Apple users need to add additional configuration - read this.

Like audioserve and want to start quickly and easily and securely? Try this simple guide to have audioserve up and running for free in no time.

TOC

Media Library

Audioserve is intended to serve files from directories in exactly same structure (with support for .m4b and similar single file audiobooks, where chapters are presented as virtual files), audio tags are not used for browsing, only optionally they can be displayed. Recommended directory structure of collections is:

Author Last Name, First Name/Audio Book Name
Author Last Name, First Name/Series Name/Audio Book Name

Files should be named so they are in right order - ideally prefixed with padded number, recently audioserve supports (by default, can be switched off by no-natural-files-ordering argument) natural ordering so padding of numbers is not necessary:

001 - First Chapter Name.opus
002 - Second Chapter Name.opus

But this structure is not mandatory - you will just see whatever directories and files you have, so use anything that will suite you.

Audioserve assumes that file and folder names are in UTF-8 encoding (or compatible), for other encodings it may not work correctly.

The characters $$ and | are used for internal usage of audioserve, so you should not use them in file names.

In folders you can have additional metadata files - first available image (jpeg or png) is taken as a cover picture and first text file (html, txt, md) is taken as description of the folder.

Search is done for folder names only (not individual files, neither audio metadata tags).

You can have several collections/libraries - just use several collection directories as audioserve command arguments. In client you can switch between collections. Typical usage will be to have separate collections for different languages.

By default symbolic(soft) links are not followed in the collections directory (because if incorrectly used it can have quite negative impact on search and browse), but they can be enabled by --allow-symlinks program argument.

Collections cache

Initially I though that everything can be just served from the file system. However experience with the program and users feedback have revealed two major problems with this approach:

  • for larger collections search was slow
  • a folder with many audiofiles (over couple hundreds) was loading slowly (because we have to collect basic audio metadata - duration and bitrate for each file)

So I implemented caching of collection data into embedded key-value database (using sled). I'm quite happy with it now, it really makes audioserve superfast.

However it brings bit more complexity into the program. Here are main things to consider:

  • On start audioserve scans and caches collection directories. If it is first scan it can take quite some time (depending on size of collection, can be tens of minutes for larger collections). Until scan is complete search might not work reliably. Also on running audioserve you can enforce full collections rescan by sending signal sigusr1 to the program.
  • Content of the cache is influenced by several program arguments, namely --tags, --tags-custom, tags-encoding, --ignore-chapters-meta, --no-dir-collaps, --allow-sym-link, -chapters-duration, --chapters-from-duration, read-playlist, --collapse-cd-folders, cd-folders-regexp, no-natural-files-ordering. If audioserve is restarted and some of these arguments is changed, it should start full collection cache reload.
  • In some cases it may happen that cache will become incosistent with file structrure in collection. Then you'll need to force full reload of cache manually - either by sending sigusr1 signal to program, or starting it with --force-cache-update argument, which enforces initial full cache reload.
  • by default audioserve is watching for collection directories changes (using inotify on linux) so if you change something in collection - add, change, rename, delete folders/files - changes will propagate to running audioserve automatically - you will just need to wait a small amount of time (app.10 seconds you can modify this by argument changes-debounce-interval, however too small value may lead to inefficient handling of changes ), before changes are visible in the program. For large collections you should increase the limit of inotify watchers in linux:
cat /proc/sys/fs/inotify/max_user_watches
# use whatever higher number matches your requirement
echo fs.inotify.max_user_watches=1048576 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
  • these contiunous updates do require some resources, firstly above mentioned kernel watches and also 3 threads per collection. You can disable watching for changes by program argument dont-watch-for-changes. Then changes will be updated only if you browse the folder (and audioserve will notice modified mtime of the folder) or assure updates manually (as metioned above like sending sigusr1 signal to program after changes or in regular interval, this will force full cache reload/rescan). Changes not updated into cache will not be available for search.

  • cache is indeed bound with collection directory path (hash of absolute normalized path is used as an identification for related cache) - so if you change collection directory path cache will also change (and old cache will still hang there - so some manual clean up might be needed).

  • if you do not want to cache particular collection you can add :no-cache option after collection directory argument. However then position sharing and metadata tags will also not work for that collection and search will be slow.

Single file audiobooks and their chapters

audioserve also supports single file audiobooks like .m4b (one big file with chapters metadata) and similar (.mp3 can also contain chapters metadata). Such file is presented as a folder (with name of original file), which contains chapters as "virtual" files (chapters behaves like other audio files - can be transcoded to lower bitrates, seeked within etc.) (if you do not like this feature you can disable with --ignore-chapters-meta argument, I have seen some .mp3 files, which contained bad chapters metadata).

Also long audiofile without chapters metadata, can be split into equaly sized parts/chapters (this has a slight disadvantage as split can be in middle of word). To enable this use --chapters-from-duration to set a limit, from which it should be used, and chapters-duration to set a duration of a part. Also for large files, which do not have chapters metadata, you can easily supply them in a separate file, with same name as the audio file but with additional extension .chapters - so it looks like your_audiobook.mp3.chapters. This file is simple CSV file (with header), where first column is chapter title, second is chapter start time, third (and last) is the chapter end time. Time is either in seconds (like 23.836) or in HH:MM:SS.mmm format (like 02:35:23.386).

For MPEG-4 containers, .m4b files and similar, cover image and description can extracted directly from file (cover is MJPEG video stream with attached picture flag, description is metadata tag named "description").

If chaptered file is a single file in a directory (and there are no other subdirectories), then chapters are presented within this directory, as if they were files in this directory and cover and description can be overriden from this directory (remplaces embeded metadata). If you do not like this feature you can disable by --no-dir-collaps option.

Also note that web client will often load same part of chapter again if you're seeking within it (especially Firefox with m4b), so it's definitely not bandwidth optimal (similar issue appears when often seeking in transcoded file).

Merge/collapsing of CD subfolders

Sometimes (mainly for historical reasons) content of audiobook is divided in CD subfolders, reflecting how it was originally distributed on physical media. In audioserve you have option to collapse all these CD subfolders into main root folder and thus see whole audiobook at once. File names then will be prefixed with CD subfolder name. This is an optional feature and could be enabled by argument --collapse-cd-folders (will require full reload of collection cache) and will collapse CD subfolders if:

Only folders matching regular expression r"^CD[ -_]?\s*\d+\s*$" (case insensitive) are collapsed into parent folder. Custom regular expression can be provided by argument --cd-folder-regexp.

Audio files metadata tags

audioserve is using directory structure for navigation and searching. This is one of key design decisions and it will not change. Main reason is because tags are just one big mess for audiobooks, everybody uses them in slightly different way, so they are not reliable. This was key reason why I started work on audioserve - to see my collection is the same way in which I stored it on disk. I do not want to bother with tags cleanup.

However with new collection cache there is possibility to scan tags and display them in web client (not yet in Android). Just for display purpose, no special functionality is related to tags.

It's optional you'll need to start audioserve with --tags or --tags-custom (here you list tags you're interested in - use --help-tags for list of supported tags).

This is the algorithm for scanning tags: tags are scanned for all files in the folder, if particular tag (like artist) is same for all files in the folder it is put on folder level, otherwise is stays with the file. For chapterized big audiofile its tags are put on folder level (virtual folder representing this file). Chapter tags are not collected (in all .m4b I saw only tag was title, which is anyhow used for chapter virtual file name).

It assumed that tags are in UTF-8 encoding, if not incorrect character is replaced by unicode replacement char. Optionally you can compile audioserve with feature tags-encoding, which will enable argument of same name - here you can provide alternate character encoding that will be used if UTF-8 decoding fails.

Collation

By default audioserve alphabetic order of audio files and subfolders is case insensitive "C like" collation, meaning national characters like "č" are sorted after all ASCII characters and not after "c". For more advanced collation respecting local collation additional unicode support is needed. Unfortunately Rust does not have native support for this and only working library is binding to ICU C libraries, which makes compilation bit complicated. To support local/national collation audioserve has to be compiled with optional feature collation. Such version of audioserve will then use following env.variables to determine locale for collation (in order of precedence): AUDIOSERVE_COLLATE, LC_ALL, LC_COLLATE, LANG. If nothing is found it falls back to en_US, which still handles somehow national characters ("č" is equal to "c" in sorting).

Playlists

With program argument read-playlist audioserve will search for file with extension m3u or m3u8 and use it to define files and their order in given folder. Content of these files in basically new line separated list of relative file paths, lines starting with # are ignored. There are these additional limitation on using playlists:

  • maximum relative path length is 4 segments
  • file must exist and be and audio file to be used
  • subdirectories that are used in playlist are not displayed, it's assumed they are fully managed by playlist
  • thoroughly tested are only playlists with items in same folder - if playlist is spanning subfolder it may have problems in some special cases like watching for directory changes, tracking playback position.

Sharing playback positions between clients

Audioserve supports sharing playback positions between clients. This is basically used to continue listening on next client, from where you left audio file on previous one. It's supported in the included web client and in the recent Android client (from version 0.8). In order to enable position sharing you'll need to define 'device group' in the client (on login dialog in web client and in settings in Android client) - group is just an arbitrary name and devices within same group will share playback position. This is not user, as there is no such concept in audioserve, it is just arbitrary identifier you set on several devices and they then share the playback position.

After you have several active devices with same group name, you'll be notified when you click play button and there is more recent playback position in the group and you can choose if to jump to this latest position or continue with current position. There is also option to check latest position directly (in web client it's icon in the folder header (shows something only if there if newer position then current), in Android client it's in options menu).

Proper functioning is (indeed) dependent on good connectivity - as position is shared during playback via web socket connection. If connection is unstable this can be unreliable or behave bit strangely.

Position tracking is tightly connected with collection cache, so it'll not work for collection, which do not use caching (specified with :no-cache option). You can also backup positions to JSON file (highly recommended) for restoration in case of disk problems or for migration of audioserve - check --positions-backup-file and --positions-backup-schedule arguments of the program. Also if former argument is present you can force immediate backup by sending signal sigusr2 to the program.

Shared playback positions are behind default program feature shared-positions, so you can compile program without it.

Shared positions also serve for marking finished / listened folders - if last file in the folder is listened till some offset from it's end (configurable via option time-to-folder-end, defaults to 10 seconds), folder if then marked as finished. If you start listening the folder again it is unmarked - folder finished flag is derived directly of last listening position in this folder.

Security

Audioserve is not writing anything to your media library, so read only access is enough. However you should assume that any file in published media directories can be accessible via audioserve API (names starting with . (hidden files/directories) are blocked in API) to anybody who can obtain shared secret (or in case you use --no-authentication then to everybody).

Read and write access is needed to data directory (~/.audioserve by default, but can be changed with --data-dir argument). This directory contains:

  • server secret - file were it keeps server secret key for authentication (by default audioserve.secret, but its locations can be changed by command line argument --secret-file) - this file should have exclusive rw access for user running audioserve (this is how file is created, so no special action is needed).
  • collections cache and playback positions - are stored in key value database, separate database is created for each collection (by default in col_db subdirectory). Collection cache database name consists of last segment of collection path and hash of absolute normalized collection path.
  • transcoding cache - optionally, if feature transcoding-cache (see below) is enabled (during compilation) cache directory (by default in audioserve-cache, can be changed by argument --t-cache-dir), where already transcoded audio files are stored for later reuse.

Authentication is done by shared secret phrase (supplied to server on command line or more securely via environment variable), which users must know. Audioserve does not have any notion of explicit named users, shared secret is all that is needed to access it (as explained above it's designed for hosting personal audio collection for one user, or group of users who trust each other fully).

Shared secret phrase is never sent in plain (it's sent as salted hash). If correct shared secret hash is provided by client, sever generates a token, using its secret key, which is then used for individual requests authentication. Token then can be used in cookie or HTTP Authorization header (Bearer method).

Token validity period is one year by default, but can be set via command line argument, but generally it is expected that token validity is in order of weeks (clients are not designed for frequent token changes).

As the token can be used to steal the session, https is recommended (TLS support is build in, but reverse proxy is probably better solution). If you want to change shared secret also delete server secret (it will invalidate all issued tokens) - stop audioserve, delete ~/.audioserve/audioserve.secret and restart audioserve with new shared secret.

Authentication is used to access all URLs except web client static files (/index.html, /bundle.js and similar).

TLS/SSL

Audioserve supports TLS/SSL - to enable it you need to provide your private server key and it's corresponding certificates chain both in PEM format (this changed recently in version 0.20 as rustls is now used, previously key and certificate were in single PKCS#12 file, I think PEM is more supported and easier to handle - it's similar how apache, nginx, etc. work, also with this change private key is no longer encrypted. Key and certificate are provided in --ssl-key and ssl-cert arguments respectively. Here is quick tip how to create private key with self-signed certificate (for testing purposed only):

openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem \
    -subj "/C=CZ/ST=Prague/L=Prague/O=Ivan/CN=audioserve"

You can also run audioserve behind reverse proxy like nginx or ha-proxy and terminate SSL there (in that case you can compile audioserve without TLS support see compilation without default features below)

Reverse proxy

Often best way how to deploy audioserve is behind reverse proxy, which terminates TLS/SSL and connects to backend audioserve. Reverse proxy can serve also other backend servers on same domain, in this case audioserve server could be determined either by subdomain ( https://audioserve.yourdomain.com), which assumes that you can modify DNS records, or by URL path prefix - external address is like https://yourdomain.com/audioserve and it's map to http://local_name_or_ip:3000 backend host. Decent proxy can do such mapping using URL rewriting (removing path prefix), but in some setups (shared seedbox), it is not possible and URL path prefix is automatically forwarded to backend. For that case audioserve has argument --url-path-prefix, which can contain path prefix (without final slash) and audioserve accepts this prefix as root path.

Another gotcha for reverse proxy might be usage of last playback position feature, which requires websocket connection and some special configuration for that might be needed in reverse proxy.

Also there is optional feature behind-proxy, which enables argument --behind-proxy and is used for logging real client ip address - client ip address is taken from Forwarded (preferred) or X-Forwarded-For HTTP headers provided by reverse proxy.

You can check some reverse proxy configurations in reverse_proxy.md (If you have successful configuration of reverse proxy please share via PR).

Limit Requests Rate

Normally you'd allow audioserve to serve as much requests as it can handle, but if you'd like to protect yourself against DDoS (Distributed Denial of Service) attack (consider how much probable and serious is this threat in your case), you should consider limiting rate of requests handling.

If audioserve is behind reverse proxy you can use rate limiting option of proxy server (like this one for nginx). Audioserve also has argument --limit-rate n, which turns on simple (it's global, not per remote address) rate limiting on all incoming HTTP requests to maximum of n request per second (approximately), for requests over this limit audioserve returns 429 - Too Many Requests HTTP status code. As this is overall limit it will not protect legal users, as they will also see rejected requests, but it will just protect host from extensive use of resources.

Number of parallel transcodings (transcodings are most resource intensive tasks) is limited by --transcoding-max-parallel-processes, which is 2 * number of CPU cores by default. This is different then limit-rate, as it guards only number of transcodings that run concurrently.

CORS

When web client is served from different host (or port) then audioserve API then browser enforces Cross-Origin Resource Sharing (CORS) rules. Basically it means that browser might refuse to connect to server, if server is not configured to send special HTTP headers.

Audioserve is handling this with with optional --cors argument, which will add appropriate CORS headers to responses, thus enabling browser to accept responses from server. If you want to limit origins for which audioserve sends CORS header you can use additional argument --cors-regex, which will first check Origin request header against given regular expresssion, if matches only then appropriate CORS responses are sent (but if you want it to use think carefully about regex - regex is not always giving what you wish for:-)

CORS is relevant in several scenarios:

  • during web client development, when client is served from development server (for instance webpack serve) on one port, say 8080, and API is served from audioserve listening on other port, say 3000 browser CORS policies will then prevent client from communicating with audioserve server API (as they are on different posts, thus different origins), unless CORS headers are included in server responses.
  • If you are using third party client (like audiosilo), client may sit in one domain, say https://client.audiosilo.app/, and audioserve in other domain, say https://audioserve.zderadicka.eu, so again here CORS headers are required (--cors argument when starting audioserve). Also in this case connection must be secure - https://.
  • Audioserve responses' CORS headers are permissive by default, allowing access from all origins and with any additional headers enable any possible scenario of usage. You can limit CORS origins by regular expression by using --cors-regex argument instead - it will allow only origins matching given regular expression.

It is important to understand that CORS is not security measure for server, but for browser only. No matter if --cors is added or not server will accept correct (properly formatted and with valid token) requests from any client.

Security Best Practices

  • Always use SSL/TLS - ideally behind well proven reverse proxy (I'm using nginx) (audioserve has support for SSL/TLS, but reverse proxy is probably more solid, plus can provide additional safeguards)
  • Set solid shared secret 14+ characters (to prevent guessing and brute force attacks), do not run on Internet with no-authentication - it's for testing only
  • Never run audioserve as root
  • Keep audioserve updated - as I'm addressing found issues in new releases (definitely do not use versions older then v0.15.0, which addressed very important security fix)
  • in $HOME/.audoserve there are files, which are writable by server - so they should have proper permissions - especially audioserve.secret should be be limited to user (running audioserve) access only
  • Never put any secret information into media directories - all content of these directories is potentially accessible via Web API.
  • Running in dedicated container also improves security
  • if using remote proxy limit audioserve listening address (--listen argument) to one reachable by remote proxy only (for instance if they are on same host use --listen 127.0.0.1:3000)
  • Optionally use your reverse proxy features like URL blocking, rate limiting etc. for additional security
  • It's good to check logs from time to time - audioserve by default logs errors (including invalid access attempts) to stderr (which can be easily redirected to file), access log of reverse proxy is also useful source of information
  • Change shared secret (ideally in some larger (months) regular intervals), but for sure change it in case you suspect it has been breached - always also delete server secret file, when changing shared secret (it will invalidate existing tokens).

Performance

Audioserve is intended to serve personal audio collections of moderate sizes. For sake of simplicity it does not provide any large scale performance optimizations. It's fine to serve couple of users from collections of couple of thousands audiobooks, if they are reasonably organized. That's it, if you're looking for solution for thousands or millions of users, look elsewhere. To compensate for this audioserve is very lightweight and by itself takes minimum of system resources.

To compensate for some slow file system operations audioserve by default is using collections cache system - see also Collections Cache above.

If transcoding is used it is another significant limiting factor - as it's quite CPU intensive. Normally you should run only a handful of transcodings in parallel, not much then 2x - 4x more then number of cores in the machine. For certain usage scenarios enabling of transcoding cache can help a bit.

Transcoding Cache

Optionally you can enable transcoding cache (by compiling audioserve with transcoding-cache feature). Contribution of this cache to overall performance depends very much on usage scenarios. If there is only one user, which basically listens to audiobooks in linear order (chapter after chapter, not jumping back and forth), benefit will be minimal. If there are more users, listening to same audiobook (with same transcoding levels) and/or jumping often back and forth between chapters, then benefits of this cache can be significant. You should test to see the difference (when transcoding cache is compiled in it can be still disabled by --t-cache-disable option).

Responses compression

Especially when network connection is slow compression of responses can help a bit. If --compress-responses argument is used, then API responses and folder descriptions will be sent compressed with gzip compression (I've tried brotli, but it makes some difference for only bigger files (>64 kB) and this is not common case in audioserve and even there it's advantage is marginal). Compression works only for non-tiny files, anything smaller then 512 bytes is left uncompressed, because it does not make any advantage to compress, on contrary it can be contra-productive. Also for static files of web client, if they already pre-compress with gzip (so they have additional extension .gz), they will be served compressed.

HTTP/2 support

Audioserve supports HTTP/2 protocol. It may have some positive impact on page load times in the browser. All current browsers require that connection is encrypted (https) in order to use HTTPS/2 protocol. If you are connecting directly to audioserve and you set it to use TLS (with --ssl-key and --ssl-cert arguments) then HTTP/2 protocol will be used automatically (for any modern browser).

If you have audioserve behind reverse proxy, then it's up to the proxy to use HTTP/2 (for in current demo https://audioserve.zderadicka.eu nginx supports HTTP/2). In this case it depends how proxy will forward requests to audioserve. audioserve (via hyper library, using magic initial line in HTTP/2 requests) can automatically distinguish between HTTP/2 and HTTP/1.1 requests. So "HTTP/2 with previous knowledge" profile will work - so if you configure your proxy to forward request using HTTP/2 protocol, audioserve should be able to handle it. However biggest advantage of HTTP/2 consists in optimizing connections from browser to remote site, I think advantage of using it also for connections between reverse proxy and audioserve will be minimal.

Audioserve does not support deprecated h2c profile (switching to HTTP/2 via HTTP/1.1 Upgrade).

audioserve also uses websocket, which is not yet supported in HTTP/2 so on reverse proxy for websocket endpoint you must ensure appropriate configuration.

Transcoding

Audioserve offers possibility to transcode audio files to opus format (opus codec, ogg or webm container) or few other formats to save bandwidth and volume of transferred data. For transcoding to work ffmpeg program must be installed and available on system's PATH. Transcoding is provided in three variants and client can choose between them (using query parameter trans with value l,m or h):

  • low - (default 32 kbps opus with 12kHz cutoff mono)
  • medium - (default 48 kbps opus with 12kHz cutoff)
  • high - (default 64 kbps opus with 20kHz cutoff)

As already noted audioserve is intended primarily for audiobooks and believe me opus codec is excellent choice there even in quite low bitrates. However if you want to change parameters of these three transcodings you can easily do so by providing yaml confing file to argument --config. Here is example of transcoding section in config file:

transcoding:
    low:
    opus-in-ogg:
        bitrate: 16
        compression_level: 3
        cutoff: WideBand
        mono: true
    medium:
    opus-in-ogg:
        bitrate: 24
        compression_level: 6
        cutoff: SuperWideBand
        mono: true
    high:
    opus-in-ogg:
        bitrate: 32
        compression_level: 9
        cutoff: SuperWideBand

In each key first you have specification of codec-container combination, currently we support opus-in-ogg, opus-in-webm, mp3, aac-in-adts (but other containers or codecs can be relatively easily added, provided they are supported by ffmpeg and container creation does not require seekable output - like MP4 container).

I have good experiences with opus-in-ogg, which is also default. opus-in-webm works well in browsers (and is supported in browsers MSE API), but as it does not contain audio duration after transcoding, audio cannot be sought during playback in Android client, which is significant drawback. mp3 is classical MPEG-2 Audio Layer III audio stream. aac-in-adts is AAC encoded audio in ADTS stream, it also may have problems with seeking in Android client.

For opus transcodings there are 3 other parameters, where bitrate is desired bitrate in kbps, compression_level is determining audio quality and speed of transcoding with values 1-10 ( 1 - worst quality, but fastest, 10 - best quality, but slowest ) and cutoff is determining audio freq. bandwidth (NarrowBand => 4kHz, MediumBand => 6kHz, WideBand => 8kHz, SuperWideBand => 12kHz, FullBand => 20kHz).

For mp3 transcoding there are also 3 parameters: bitrate (in kbps), compression_level with values 0-9 (0 - best quality, slowest, 9-worst quality, fastest; so meaning is inverse then for opus codec) and abr (optional), which can be true or false (ABR = average bit rate - enables ABR, which is similar to VBR, so it can improve quality on same bitrate, however can cause problems, when seeking in audion stream).

aac-in-adts has one mandatory parameter bitrate (in kbps) and two optional parameters sr - which is sample rate of transcoded stream (8kHz, 12kHz, 16kHz, 24kHz, 32kHz, 48kHz, unlimited) and ltp (Long Term Prediction), which is true or false and can improve audio quality, especially for lower bitrates, but with significant performance costs ( about 10x slower).

All encodings have optional parameter mono, if set to true audio will be down-mixed to mono.

You can override one two or all three defaults, depending on what sections you have in this config file. You can also provide complete alternative transcoding configuration for particular clients (see below)

Overall opus-in-ogg provides best results from both quality and functionality perspective, so I'd highly recommend to stick to it, unless you have some problem with it, which might be case on Apple platforms (see below).

Alternative transcodings and transcoding configuration for Apple users

Default transcoding for audioserve is opus codec in ogg container, which is not supported on Apple platforms. Recently audioserve also supports alternative transcoding configurations based on matching User-Agent string in request header. You can create any number of alternative transcoding configurations, each identified by a regular expression. First matching configuration is then used.

So if you create this configuration file:

---
transcoding:
  alt_configs:
    "iPhone|IPad|Mac OS":
      low:
        aac-in-adts:
          bitrate: 32
          sr: "24kHz"
          mono: true
      medium:
        aac-in-adts:
          bitrate: 48
          mono: false
      high:
        aac-in-adts:
          bitrate: 64
          mono: false

and use it with audioserve through argument --config or short version -g. It will then use aac transcoding for browsers on Apple platforms.

Command line

Audioserve can take parameters from command line, environment variables and config file. For command line arguments check them with audioserve -h. Generally you need to provide shared secrect (or option --no-authentication for public access) and media collection directory (as noted above you can have severals collections). You can also provide options specific for particular collection directory (add : and options directly after the collection path). For details use help-dir-options argument.

audioserve is server executable and it also needs web client files , which are index.html and bundle.js, which are defaultly in ./client/dist, but their location can by specified by argument -c.

For majority of command line arguments there are also appropriate environment variables, which start with prefix AUDIOSERVE_ and then command line argument name (without leading dashes) transcribed from kebab-case to SCREAMING_SNAKE_CASE, so for instance argument --shared-secret has corresponding env. variable AUDIOSERVE_SHARED_SECRET.

All audioserve parameters can be also provided in configuration file via --config argument. Configuration file is in YAML format and somehow resembles command line arguments, but not exactly (main difference is dashes are replaced by underscores). Easiest way how to create config file is to use argument --print-config, which prints current configuration, including all used arguments to standard output.

Web client

Web client resides in it's own project and it's integrated into Docker image build, so it's part of the image. New web client uses latest and greatest web technologies and it's intended to replace old Android client (can be installed as PWA app), supports tracks caching and offline mode.

I'm testing web client on recent Firefox and Chrome/Chromium (on Linux and Android platforms, occasionally on Win and Edge, assuming that Edge is now basically Chrome, so it should work). For Apple platforms, new client should work for Safari after some additional configuration - check this chapter.

Android client

Android client code is available on github, but is not maintained any more, it should still work, but I do not test it any more. I believe web client can fully replace it (with few minor things that really require platform integration).

API

audioserve server provides very simple API, defined in OAS 3 (see also api.md for details), so it's easy to write your own clients.

Installation

Docker Image

Easiest way how to test audioserve (but do not use --no-authentication in production) is to run it as docker container with prebuild Docker image (from Docker Hub). To quickly test audioserve run:

RUST_LOG=info docker run --rm -it --name audioserve -p 3000:3000 -v /path/to/your/audiobooks:/audiobooks  izderadicka/audioserve --no-authentication /audiobooks

You should see some basic audioserve log. Then open http://localhost:3000 - and browse your collection. This is indeed the very minimal configuration of audioserve and should not be used in production. For real deployment you'd like provide provide more command line arguments (or environment variables or your custom config file) and it's essential to map persistent volume or bind writable host directory to audioserve data-dir (defaulted to /home/audioserve/.audioserve or can be set via --data-dir argument) - see more complex example below.

There is also izderadicka/audioserve:unstable image, which is automatically built overnight (if code changes) from current master branch (so it contains latest features, but may have some issues). And of course you can build your own image very easily with provided Dockerfile, just run:

docker build --tag audioserve .

When building docker image you can use --build-arg CARGO_ARGS= to modify cargo build command and to add/or remove features (see below for details). For instance this command will build audioserve with transcoding cache docker build --tag audioserve --build-arg CARGO_ARGS="--features transcoding-cache" . If you want to build a debug version supply this build argument --build-arg CARGO_RELEASE="".

There is also one additional env. variable PORT - TCP port on which audioserve serves http(s) requests (defaults to: 3000) - this is useful for services like Heroku, where container must accept PORT variable from the service.

A more realistic docker example (audioserve executable is an entry point to this container, so you can use all command line arguments of the program, to get help for the program run this command docker run -it --rm izderadicka/audioserve --help):

docker run -d --name audioserve -p 3000:3000 \
    -v /path/to/your/audiobooks1:/collection1 \
    -v /path/to/your/audiobooks2:/collection2 \
    -v /path/for/audioserve-data:/home/audioserve/.audioserve \
    -e AUDIOSERVE_SHARED_SECRET=mypass \
    izderadicka/audioserve \
    --tags \
    --behind-proxy \
    --transcoding-max-parallel-processes 24 \
    --positions-backup-file /home/audioserve/.audioserve/positions-backup.json \
    --positions-backup-schedule "0 3 * * *" \
    /collection1 /collection2

In the above example, we are adding two different collections of audiobooks (collection1 and collection2). Both are made available to the container via -v option and then passed to audioserve on command line. Also we have mapped with -v some folder to /home/audioserve/.audioserve, where runtime data of audioserve are stored (server secret, caches ...). For production it's essential to map this volume either to host directory (which must have read and write permissions for audioserve user, id 1000 by default) or to named volume.

We set the shared secret via AUDIOSERVE_SHARED_SECRET env.variable and also set couple of other arguments:

  • --tags to scan and cache common metadata tags.
  • behind-proxy just improves logging - logs real client IP address if audioserve is behind reverse proxy
  • --transcoding-max-parallel-processes increases a bit number of parallel transcoding allowed
  • --positions-backup-file and --positions-backup-schedule backs up playback positions to open, transferrable JSON file

Running audioserve in Docker as different user

By default audioserve is running as user uid 1000, which is fine in many use cases (uid 1000 is default primary user on Debian, Ubuntu, Alpine linux, but for instance not on CentOS or RHEL). But sometimes you'd like to run audioserve as different user (say uid 1234), for this you must:

  • run docker with --user 1234
  • assure that uid 1234 has read access to your collection folder (others have r access to files and rx to directories)
  • create directory with read and write access for uid 1234 (or optionally use named volume)
  • map that directory as volume to docker -v /my/audioserve/data-dir:/audioserve-data
  • and add this argument to audioserve --data-dir /audioserve-data

Docker Compose Example

Here is a simple docker-compose example that enables tags caching

---
version: "3"

services:
  audioserve:
    image: izderadicka/audioserve
    restart: unless-stopped
    command: --tags /audiobooks
    environment:
      - PUID=1000
      - PGID=1000
      - "AUDIOSERVE_SHARED_SECRET=VGM4oDS6wGKge9"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - "./config:/home/audioserve/.audioserve"
      - "/path/to/Audio/Books:/audiobooks"

Static build (Linux)

Static build of audioserve is available (for recent releases) at github releases page. You can can just download and extract it locally and run on any modern x86_64 linux, but it does not contain the web client - you need to get latest release.

You can also create your own static build with script build_static.sh (Docker is required for this)

Local build (Linux)

Now audioserve depends on ffmpeg's libavformat 4.3/4.4 or 5.0/5.1 (and its dependent libavutil and libavcodec libs), which is a complex beast. If you are building locally you need this dependence (plus couple of others). If you have available right versions on your system you can dynamically link against it (remember it has to be correct version, if you have wrong wersion you'll probably see Segmentation Faults when running the program). Other option is to use feature partially-static, which will download right version of ffmpeg, compile it and statically link it into audioserve (but then binary will be indeed bigger).

Install required dependencies (some dependencies are optional, depending on features chosen in build):

# Ubuntu 20.04 - for other distros look for equivalent packages
sudo apt-get install -y  git pkg-config \
    ffmpeg yasm build-essential curl wget libbz2-dev zlib1g-dev libavformat-dev \
    clang coreutils exuberant-ctags gawk  libclang-dev llvm-dev strace libicu-dev

Clone repo with:

git clone https://github.com/izderadicka/audioserve

To install locally you need recent Rust and NodeJS installed.

Compile Rust code (it has optional system dependencies to zlib, bz2lib, and libavformat, as might not have exactly correct version of libavformat around, it's better to build required version statically into binary with partially-static feature, because otherwise you might see problems like segfaults):

cargo build --release --features partially-static

Optionally you can compile with/without other features (see below for details).

Build new client:

git clone https://github.com/izderadicka/audioserve-web.git /audioserve_client &&\
    cd /audioserve_client &&\
    npm install &&\
    npm run build &&\
    npm run build-sw &&\
    mv public dist  # The directory that audioserve will recognize as --client-dir

Alternativelly you can build old client (which is still in this repo, chage to its directory cd client):

npm install
npm run build

Other platforms

Linux is the only officially supported platform. It can theoretically work on other platforms (win, MacOS), but it might require some changes in build process and probably also small changes in code. A contributor tried build for windows with partial success, so you can checked that.

Any help in this area is welcomed.

Compiling without default features or with non-default features

TLS support (feature tls), symbolic links (feature symlinks), shared playback positions (feature shared-positions), enhanced logging, when behind proxy (feature behind-proxy) and folder download (feature folder-download) are default features, but you can compile without them - just add --no-default-features option to cargo build command. And then eventually choose only features you need. To add non-default features (like transcoding-cache) compile with this option --features transcoding-cache in cargo build command.

Available features:

Feature Description Default Program options
tls Enables TLS support (e.g https) Yes --ssl-key --ssl-cert to define server key and certificate
symlinks Enables to use symbolic links in media folders Yes Use --allow-symlinks to follow symbolic links
folder-download Enables API endpoint to download content of a folder in tar archive Yes Can be disabled with argument --disable-folder-download
shared-positions Clients can share recent playback positions via simple websocket API Yes
behind-proxy Enable logging of client address from proxy headers yes Enables argument --behind-proxy which should be use to log client address from headers provided by reverse proxy
transcoding-cache Cache to save transcoded files for fast next use No Can be disabled by --t-cache-disable and modified by --t-cache-dir --t-cache-size --t-cache-max-files
static Enables fully static build of audioserve. Check above notes for static build No
partially-static Statically links libavformat (and related).Enables to run audioserve on systems, which do not have required version of libavformat No
folder-download-default-tar Default folder download format is tar (instead of zip) No
collation or collation-static Supports locale collation (for static build second option must be used!) No Env. variables AUDIOSERVE_COLLATE, LC_ALL, LC_COLLATE, LANG determine locale used
tags-encoding Enables alternate charactacters encoding for audio metadata tags No Enables argument --tags-encoding

License

MIT

audioserve's People

Contributors

bougakov avatar izderadicka avatar jkuettner avatar mpatton125 avatar muggenhor avatar naglis avatar renovate[bot] avatar srd424 avatar zachmyers3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

audioserve's Issues

Simple editable list support

I use Syncthing to move my audiobooks to my mobile for offline play - I'm not interested in a streaming client, it would cost me too much (although my brother would use it). But I'd like to have a way to manage my library online, and audioserve looks almost perfect for that.

First, would it be possible to think about adding booklists, or shelves, that users could add and remove books to? The minimum I want is a "currently reading" shelf.

Second, would it be possible to provide some minimal API (maybe RESTlike) to get a user's shelf contents?

iOS devices - playback error

Hi,

I'm trying to access AudioServe using iOS Safari on a 2017 IPad (latest iOS release) and get a playback error. The logs in docker show this, though I am not sure it's directly related:

WARN audioserve::services::transcode > Transconding of file "/audiobooks/file.mp3" failed with code Some(1)

The same file plays on android and MacOS desktop as well as Windows.

Any help would be very appreciated.

New Web Client

Opening new issue for new web client. Current one has definitely issues and @KodeStar started to work on new one.

Static compilation

I'm having problems with full static compilation of audioserve due to usage of C++ library libtag. Some details are described here
emk/rust-musl-builder#65. Looks like C++ and Rust have some friction when compiled together statically. If anyone knows a good way out please let me know, as static compilation would be beneficial for deployment (really lean docker image or direct binary download).

[security] Shared Secret Mechanism is ineffectual

As you've already mentioned in the README:

As the token can be used to steal the session https is recomended (TLS support is build in).

So the whole shared secret calculation doesn't really improve the security in any way. A HTTP session is always going to be vulnerable to replay attacks in such scenarios and the best you can do is to make sessions short lived so stolen sessions don't last long.

However, seeing as that is not the case, I'd suggest replacing the complicated shared secret calculation with a simple Bearer Token Auth or Basic Auth instead.

Please don't roll your own crypto. You're already pushing people to TLS, which is the right solution to the problem here.

Feature Request: PWA support

It would be great if the page also "exposed" itself as a PWA, so that it could be added as an "app" in IOS, and Android.

Change Dockerfile to create smaller image

Current Dockerfile creates image with size over 2GB. It's just too much!

Try to come with something smaller - using Ubuntu as base is not efficient, majority of things is just not needed.
audioserve compiles to one binary plus two additional files are needed (index.html and bundle.js for web client).
audioserve depends on few dynamic libraries:

ldd /opt/audioserve/audioserve
	linux-vdso.so.1 =>  (0x00007ffdd6df4000)
	libssl.so.1.0.0 => /lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007f848886f000)
	libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f8488492000)
	libtag_c.so.0 => /usr/local/lib/libtag_c.so.0 (0x00007f8488289000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f8488085000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f8487e7d000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8487c5f000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8487a49000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8487680000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f8489219000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f848737a000)
	libtag.so.1 => /usr/local/lib/libtag.so.1 (0x00007f8486fc9000)
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f8486cc5000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f8486aac000)

Try complely static build with musl libc? Then it can use alpine linux?

Client fails to load with hostname in URL, ip works fine.

URL with hostname: No prompt, just the animation
URL with IP: works fine.
hostname resolves to the IP on both server and client.
hostname resolves to a different IP in public domain

Audioserve did not log anything in std or err.
I am not proficient with web debug to go further.

I will try to find out more and update the issue.

Secret not working and Android unable to connect

Firstly, I really like what's being done with this tool, and especially the app. If I can get it up and running I think it'll be perfect for me. So, thanks for making it!

My setup is running in docker with a reverse proxy, which works fine to serve up the site, but the shared secret seems to be ignored entirely. The audioserve.secret key is being created and there is nothing in the logs to indicate any issues.

I tried opening the port to the web and it still ignores the shared secret so it seems like it's not an issue with the reverse proxy.

Additionally, and perhaps this is somewhat unrelated, when trying to connect to the server through the Android app I get a generic server error message, whether or not I have set a shared secret.

If I go to the site to test out the response from the API manually, I get expected responses except where I go to /authentication and simply get 'Not Found'.

As an aside it would be super useful to serve the API requests from a shared sub-directory such as /api so I could easily add different auth settings for the API. :)

Base URL

Is there an option to configure the base URL to use audioserve when using a reverse proxy? I can't find one in the configuration.

Noob question: How to update?

I used this command to install audioserve

docker run -d --name audioserve -p 3000:3000 -e AUDIOSERVE_SHARED_SECRET=mysecret -v /home/myusername/audiobooks:/audiobooks izderadicka/audioserve /audiobooks

How to I update to the newest version? I could not find any documentation. Thanks for your help. I am a noob, still learning Linux and Docker

Feature Request: Log Failed Logins

Request: output failed login attempt with IP and timestamp to a log folder.
Reason: this will allow a user to block an ip for those that expose the server publicly.

Side note: Thanks for this project! I've tried lots of other options and this one is truly KISS.

config file

As there are many program arguments add support for config file - with override by actual command line arguments.

Docker improvements

Hello, i think docker image can be better if:

  1. Accessible from hub.docker.com
  2. Provides parameters for enable/disable https, enable/disable passkey, set custom passkey

Hope project is not dead

Media With Trailing Spaces Not Accessible

Hi,

thanks for developing audioserve, it's exactly the tool I was looking for.

Running audioserve 0.15.4 in a docker environment without a special custom config.

I accidentally put a trailing space into a folder name, this made the folder inaccessible through the web interface and I jumped right to the start page.

Not a major thing, but anyway, spaces should not break the system, right? 😄

Thanks again and have a nice day.

Support for rendering or downloading PDF/html

I have a collection of language learning audibooks and podcasts that often come with PDF (grammar guide, scan of a book) and html (transcript) files that you supposed to look at while you are listening.

It would be nice to a have an ability to view those files in a browser or at least be able to download them.

Example:

.
├── 01. Who is That Japanese Person  - Audio.mp3
├── 02. Would You Like a Japanese Gift or Not  - Audio.mp3
├── 03. I Don't Know Why This is Happening in Japan - Audio.mp3
│   ...
├── pdf
│   ├── 01. Who is That Japanese Person  - Kanji Close-Up.pdf
│   ├── 01. Who is That Japanese Person  - Lesson Notes.pdf
│   ├── 02. Would You Like a Japanese Gift or Not  - Kanji Close-Up.pdf
│   ├── 02. Would You Like a Japanese Gift or Not  - Lesson Notes.pdf
│   ├── 03. I Don't Know Why This is Happening in Japan - Kanji Close-Up.pdf
│   ├── 03. I Don't Know Why This is Happening in Japan - Lesson Notes.pdf
│   ...
└── trans
    ├── 01. Who is That Japanese Person  - Line-By-Line Audio Transcript.html
    ├── 02. Would You Like a Japanese Gift or Not  - Line-By-Line Audio Transcript.html
    ├── 03. I Don't Know Why This is Happening in Japan - Line-By-Line Audio Transcript.html
    ...

Audiofile (.mp3) showing up as a subfolder and not a file.

Hey there, enjoying the software, its exactly what I need.

I have come across an issue with a few files though. For some reason, I have a mp3 file which keeps displaying as a subfolder. I thought it had an extra stream in, when I click on the .mp3 name it then takes me to another sub directory where I can play the actual mp3 file and a random non-playable file.

It should just play my mp3 file like the others. I have included a few snapshots below. Most other MP3 files work fine. Also included is an output of ffprobe with two files, one of which displays as a file, the other as a sub-folder. Only difference I can see is the encoder tag. Any help would be great.

image

image

ffprobe output for working file

    "streams": [
        {
            "index": 0,
            "codec_name": "mp3",
            "codec_long_name": "MP3 (MPEG audio layer 3)",
            "codec_type": "audio",
            "codec_time_base": "1/44100",
            "codec_tag_string": "[0][0][0][0]",
            "codec_tag": "0x0000",
            "sample_fmt": "fltp",
            "sample_rate": "44100",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/14112000",
            "start_pts": 353600,
            "start_time": "0.025057",
            "duration_ts": 11389501440,
            "duration": "807.079184",
            "bit_rate": "128000",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "encoder": "LAME3.96r"
            },
            "side_data_list": [
                {
                    "side_data_type": "Replay Gain"
                }
            ]
        }
    ],
    "format": {
        "filename": "Sylvia Plath  - The Bell Jar CD1 - Track 01.mp3",
        "nb_streams": 1,
        "nb_programs": 0,
        "format_name": "mp3",
        "format_long_name": "MP2/3 (MPEG audio layer 2/3)",
        "start_time": "0.025057",
        "duration": "807.079184",
        "size": "13027567",
        "bit_rate": "129132",
        "probe_score": 51,
        "tags": {
            "disc": "1/7",
            "track": "1/38"
        }
    }
}```

ffprobe output for file showing as a subfolder
```json{
    "streams": [
        {
            "index": 0,
            "codec_name": "mp3",
            "codec_long_name": "MP3 (MPEG audio layer 3)",
            "codec_type": "audio",
            "codec_time_base": "1/44100",
            "codec_tag_string": "[0][0][0][0]",
            "codec_tag": "0x0000",
            "sample_fmt": "fltp",
            "sample_rate": "44100",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/14112000",
            "start_pts": 353600,
            "start_time": "0.025057",
            "duration_ts": 9596805120,
            "duration": "680.045714",
            "bit_rate": "128000",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "encoder": "Lavc58.91"
            }
        }
    ],
    "format": {
        "filename": "Part 0 - Intro.mp3",
        "nb_streams": 1,
        "nb_programs": 0,
        "format_name": "mp3",
        "format_long_name": "MP2/3 (MPEG audio layer 2/3)",
        "start_time": "0.025057",
        "duration": "680.045714",
        "size": "10885633",
        "bit_rate": "128057",
        "probe_score": 51,
        "tags": {
            "major_brand": "isom",
            "minor_version": "512",
            "compatible_brands": "isomiso2mp41",
            "comment": "",
            "copyright": "Penguin Random House Audio Publishing Group",
            "media_type": "2",
            "genre": "African American Nonfiction/Biography & Autobiography/Nonfiction/Politics",
            "disc": "0",
            "track": "1",
            "title": "Part 0 - Intro",
            "artist": "Barack Obama",
            "album": "A Promised Land",
            "album_artist": "A Promised Land",
            "encoder": "Lavf58.45.100"
        }
    }
}```

Folder Picture

Hello,

I love your so smart but simple server. I am hosting a lot of audio books for my kids. They haven't learned reading so far and I wonder if you could read out of the Folder a picture (jpg file or mp3 meta) and position it before folder name?

Thanks for your effort

Docker image shutting down after a few days

I'm running the service inside the official docker image and proxy it via nginx + let's encrypt. After a few days the process exits with status code 0 and no log output, making nginx shut down (for domain name reasons with docker-compose; but that's not the issue here).

Is there some way I can try to debug this problem? I'm pretty confident in Rust, but it's also running on a server with limited hardware, so recompiling constantly is probably not going to work for me.

It's happened four times now, so I'm now turning on restart: unless-stopped to work around it, but getting to the cause of the issue would be preferred by me.

The little information I can give:

  1. Process had no output. Exit status 0.
  2. The most I've been able to have the container running at a time: Around 3 days.
  3. Service works well when it's running (although it emits a lot of warnings about upgrading websockets.
  4. Memory pressure on the server is fine. It was not killed by OOM killer according to docker inspect either.
  5. CPU usage has been low up until the service died.
  6. No problem with disk space.
  7. Server has had no network interruptions during this time.
  8. No other container (I run around 11 of them) has any similar issues.
  9. Server is my own hardware at home, not a cloud host or a VM. No hardware issues that I know of, and it's pretty new.

I'm sorry I cannot give any more information. This "issue" is literally me asking for ideas on what to look for so I can give you better information.

Synchronize playback position between players

I've seen this requirement in discussion on reddit. It would be surely cool functionality, but not easy to implement. My initial thoughts:

No immediate plans in audioserve. It'll require significant change+ new functionality. Theoretically I was thinking about separate service/process, where each player can publish position (using WebSocket connection while playing?). And get updates when starts to play after pause. Then decides if wants to continue from local position or skip to latest remote position. Something like this. But this will require a lot of work.

Feature Request: Dark theme & cover view

Hey, great work on this project, loving it so far! It Would be nice to have a dark theme option. Also it would be nice to have the option to display the cover images if there is one present in the directory instead of the directory name. Cheers!

MS Edge browser is not working

JS error SCRIPT5009: 'TextEncoder' is not defined.
This error appears during authentication.

Edge is definitely not priority, but if anyone wants to look at it.

Feature Request: Mark tracks with audio problems

I completely understand that this may be something that isn't often thought of or needed, but it would be great to be able to mark a track that has audio errors in it such as clicking or popping.

Upgrade to new futures and async/await style

New futures made it to std, new hyper(0.13) and tokio(0.2) are also out, so it's time to update. There would be quite a lot of changes so I assume significant effort.

Unfortunately right now I do not have much time, but this is high priority for sure.

Spacebar pause/play

It would be nice to be able to use the spacebar as shortcut to play and pause the audiobook in the web client.

Docker Auto-update

Linuxserver.io dockers usually auto update the application contained inside. IF the Audioserve docker does not, could you add that (I'm sure LS.io guys would be happy to help with pointers!)

Disable loading meta info when open filder

Thanks for your audio server. This is exactly what I am looking for: a file based stream server for audio books.

However, I have some folders contains more than 500 files. It takes over 1 min to open that folder and Android app just timeout and make it useless.

Is it possible to disable the loading meta code when I open the folder? I think for folder based approach, the meta data can be totally ignored until the open time.

Thanks

Allow caching of entire folder

Often, simply downloading the entire audiobook instead of having to have a connection to the server all the time is useful, if you know you aren't going to be near a connection all the time.

Playback speed

It would be nice to have the ability to set the playback speed per audiobooks. I like to listen at faster speed than 1x.

Implement Subsonic API for audioserve

This can help with limited clients - especially mobile clients - as there are already couple of Subsonic API clients.
Not sure if we can match Subsonic API to audioserve, but at least it worth to research.

Error creating secret permission denied -- docker linux

Trying to create a docker container based on the izderadicka/audioserve:latest I continually run into a permission denied error for creating/reading the secret. I have about 8 other containers that right to similar directories using the same PGID+PUID without error (everything being under a directory for the docker user each service having it's own "config" folder). I've tried using the synology GUI updating all the info and command, I've tried sudo and nonsudo docker run commands via SSH, and tried creating the run-audioserve.sh file in the setup guide all to no avail. Does the container setup accept PUID/PGID as environment inputs or does a hardcoded PUID have to be changed somewhere to the correct value for access to said directories?

Improve security

Recently I have been thinking bit more about security of audioserve. And yes I have found one nasty issue (fixed now from v0.15.0) and that motivated me to give more thought to security.

audioserve is not meant to be super secretive, military grade. I'm assuming that people are just streaming audiobooks (or other audio) and that is no big deal, nothing really clandestine. But on the other hand audioserve should be solid and should not allow it's misuse to endanger its hosting system.

So what I have done recently:

  • fix bug
  • added more information related to security and best practices
  • added more detailed logging of authentication failures (including client IP address)
  • added wait time after detecting incorrect shared secret - to slow down potential brute force attacks
  • added possibility to limit rate of http requests - to protect system in case of DDoS (also you can solve this on reverse proxy - probably better, but thought it would be nice to have it in audioserve too). BTW number of transcodings (which are most resource intesive) is limited already - from very first version of audioserve.

But I'd like to also get some feedback from users - so please share here:

WHAT ARE YOUR SECURITY ISSUES OR CONCERNS OR QUESTIONS?

(I'll keep this issue opened for while to collect feedback).

Building for Windows 10 - 'cannot run ffmpeg build script'

Hi. I've tried to build the project on Windows.

I've downloaded 64-bit distribution of Rust and installed it. I've also downloaded and installed Build Tools for Visual Studio 2019 (to obtain link.exe).

I've also downloaded an FFmpeg build with shared libraries and saved its contents straight to the Windows folder.

Build process breaks at media_info (audioserve builds just fine):

C:\Users\sanja\Downloads\audioserve-master\audioserve-master>cargo build --release --features partially-static
   Compiling winapi v0.3.8
   Compiling proc-macro2 v1.0.9
   Compiling syn v1.0.16
   Compiling memchr v2.3.3
   Compiling fnv v1.0.6
   Compiling winapi-build v0.1.1
   Compiling log v0.4.8
   Compiling libc v0.2.67
   Compiling lazy_static v1.4.0
   Compiling bytes v0.5.4
   Compiling slab v0.4.2
   Compiling winapi v0.2.8
   Compiling futures-core v0.3.4
   Compiling byteorder v1.3.4
   Compiling itoa v0.4.5
   Compiling proc-macro-nested v0.1.3
   Compiling typenum v1.11.2
   Compiling futures-sink v0.3.4
   Compiling iovec v0.1.4
   Compiling serde v1.0.104
   Compiling pin-project-lite v0.1.4
   Compiling pin-utils v0.1.0-alpha.4
   Compiling futures-io v0.3.4
   Compiling futures-task v0.3.4
   Compiling getrandom v0.1.14
   Compiling byte-tools v0.3.1
   Compiling bitflags v1.2.1
   Compiling version_check v0.9.1
   Compiling ident_case v1.0.1
   Compiling strsim v0.9.3
   Compiling autocfg v1.0.0
   Compiling matches v0.1.8
   Compiling httparse v1.3.4
   Compiling smallvec v1.2.0
   Compiling ppv-lite86 v0.2.6
   Compiling fake-simd v0.1.2
   Compiling opaque-debug v0.2.3
   Compiling base64 v0.11.0
   Compiling quick-error v1.2.3
   Compiling ryu v1.0.2
   Compiling percent-encoding v2.1.0
   Compiling cc v1.0.50
   Compiling utf-8 v0.7.5
   Compiling derive_builder v0.9.0
   Compiling try-lock v0.2.2
   Compiling native-tls v0.2.3
   Compiling mime v0.3.16
   Compiling unicode-width v0.1.7
   Compiling linked-hash-map v0.5.2
   Compiling media_info v0.1.4 (C:\Users\sanja\Downloads\audioserve-master\audioserve-master\crates\media_info)
   Compiling tower-service v0.3.0
   Compiling regex-syntax v0.6.14
   Compiling dtoa v0.4.5
   Compiling vec_map v0.8.1
   Compiling strsim v0.8.0
   Compiling ego-tree v0.6.2
   Compiling untrusted v0.7.0
   Compiling spin v0.5.2
   Compiling bit-vec v0.6.1
   Compiling audioserve v0.12.1 (C:\Users\sanja\Downloads\audioserve-master\audioserve-master)
   Compiling data-encoding v2.2.0
   Compiling kernel32-sys v0.2.2
   Compiling ws2_32-sys v0.2.1
   Compiling thread_local v1.0.1
   Compiling input_buffer v0.3.1
   Compiling http v0.2.0
   Compiling futures-channel v0.3.4
   Compiling block-padding v0.1.5
   Compiling unicase v2.6.0
   Compiling unicode-bidi v0.3.4
   Compiling indexmap v1.3.2
   Compiling unicode-normalization v0.1.12
   Compiling humantime v1.3.0
   Compiling c2-chacha v0.2.3
   Compiling textwrap v0.11.0
   Compiling yaml-rust v0.4.3
error: failed to run custom build command for `media_info v0.1.4 (C:\Users\sanja\Downloads\audioserve-master\audioserve-master\crates\media_info)`

Caused by:
  process didn't exit successfully: `C:\Users\sanja\Downloads\audioserve-master\audioserve-master\target\release\build\media_info-8e411d9664ae1e6e\build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'cannot run ffmpeg build script: Os { code: 193, kind: Other, message: "%1 is not a valid Win32 application." }', crates\media_info\build.rs:17:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

warning: build failed, waiting for other jobs to finish...
error: build failed

Unfortunately, I have zero experience with Rust, so I can only help with details but not with fixes. Any ideas on how to solve this? Thanks.

ERR_CONNECTION_REFUSED

I've installed the docker, ran the command but when I try and access my server on port 3000 I get "This site can’t be reached" - what could I be doing wrong?

When I run "docker ps" I can see that audioserve IS running on port 3000

Enhancements to the API

Aggregated info
Book name
Book author
Total time
sha256 hash of folder contents

Group details
Such as

{
  "Kode": {
    "playback_speed": "1.5",
    "active_books": [
      {
        "name": "Artemis Fowl",
        "author": "Eoin Colfer",
        "folder": "Eoin Colfer/Artemis Fowl/AF01 - Artemis Fowl",
        "cover": "cover/Eoin Colfer/Artemis Fowl/AF01 - Artemis Fowl/Artemis Fowl 1 - Artemis Fowl.jpg",
        "current_position": "19827",
        "total_time": "60481",
        "hash": "263bc4f19f5e291fce24e6d4a6e4442e6f893cbabbcc737f105dff8d2cf8d845"
      },
      {
        "name": "Billionaire Boy",
        "author": "David Walliams",
        "folder": "David Walliams/Billionaire Boy",
        "cover": "cover/David Walliams/Billionaire Boy/Billionaire Boy.jpg",
        "current_position": "19827",
        "total_time": "60481",
        "hash": "123bc4f19f5e291fce24e6d4a6e4442e6f893cbabbcc737f105dff8d2cf8d375"
      }
    ],
    "finished_books": [
      "961bc4f19f5e291fce24e6d4a6e4442e6f893cbabbcc737f105dff8d2cf8d219",
      "66fc3b5726ad08f34ec7e6f9952e889d1eec1efc9180c8c48fc2e8c35373efc7"
    ]
  },
  "Bob": {
    "playback_speed": "1.2",
    "active_books": [
      {
        "name": "Artemis Fowl",
        "author": "Eoin Colfer",
        "folder": "Eoin Colfer/Artemis Fowl/AF01 - Artemis Fowl",
        "cover": "cover/Eoin Colfer/Artemis Fowl/AF01 - Artemis Fowl/Artemis Fowl 1 - Artemis Fowl.jpg",
        "current_position": "19827",
        "total_time": "60481",
        "hash": "263bc4f19f5e291fce24e6d4a6e4442e6f893cbabbcc737f105dff8d2cf8d845"
      }
    ],
    "finished_books": [
      "961bc4f19f5e291fce24e6d4a6e4442e6f893cbabbcc737f105dff8d2cf8d219",
      "66fc3b5726ad08f34ec7e6f9952e889d1eec1efc9180c8c48fc2e8c35373efc7",
      "8e5fe3893ab85cdaa606ef27061cba04238ed49e05431018fe7d6f47711f734e"
    ]
  }
}

Folder playback status
Grouped by
API endpoints for start and stop that log to a history, something like:

{
  "Kode": {
    "current_position": "19827",
    "history": [
      {
        "start": "1631086131",
        "startSeek": "19327"
        "finish": "1631586131",
        "finishSeek": "19827"
      },
      {
        "start": "1633086131",
        "startSeek": "19827"
        "finish": "1633586131",
        "finishSeek": "20327"
      }
    ]
  },
  "Bob": {
    "current_position": "19827",
    "history": [
      {
        "start": "1631086131",
        "startSeek": "19327"
        "finish": "1631586131",
        "finishSeek": "19827"
      },
      {
        "start": "1633086131",
        "startSeek": "19827"
        "finish": "1633586131",
        "finishSeek": "20327"
      }
    ]
  }
}

This would work for playback on other devices as well, rather than relying on websockets, just record the latest position, then on another device load up the group information and continue from the position. Having the history is useful for situations where playback has started accidentally, you can go to your history and start playback from the last location.

Feature request: similar functionality for MP4 / AVI video files

Audioserve is very helpful for listening to audiobooks hosted on a local file server.

But in addition to the audiobooks I have lots of educational courses in MP4 or AVI format. They are also organized in folders, such as Topic 03 / Chapter 11.mp4

I'd be extremely happy if I could watch those courses in a browser in a similar manner. Can you please add support for streaming them directly, without transcoding? I presume that the required changes are minimal and require only adding a JavaScript video player to the client, such as https://plyr.io/, https://videojs.com/ or http://jplayer.org/ Thanks in advance.

Users? Or multiple secrets & associate bookmarks to them?

First off, fantastic project! This is almost exactly what I’ve been looking for. One issue... users. I respect that there is a desire to keep things simple and just use one secret. However, if I share my library with my family, bookmarks are going to get jumbled up. How about having multiple secrets and associating bookmarks with a secret? Or perhaps a simple user,pass csv file? It would be nice if the small number of users I’d grant access to the server would each have their own bookmarks.

Prebuilt windows release

I am with my current setup not able to run docker, it would thus be really helpful to have a prebuilt windows release available, especially since the current instructions don't work according to some other issues.

Preflight CORS requests are not hadled correctly

When using with --cors header and other web client, served from different location then API, and which sends preflight requests , then
OPTIONS preflight for /authenticate return 401 status,
thus
client/web browser cannot access the endpoint

OPTIONS requests handling has to be fixed in audioserve - especially when --cors switch is on.

Websocket errors in error logs

My logs are full of these messages:

 ERROR websock > Upgrade is not to websocket
 ERROR websock > It must be upgrade connection

I'm running audiosurf behind an nginx proxy. Do I need to do something special to get WS to work behind there?
I'm on HTTPS-only.


Originally posted by @Mange in #16 (comment)

unRAID Template

Any chance of creating an unRAID template and share it to Community Applications?

Or an easy guide to installing it on unRAID.

Add support for m4b single file, large mp3

One thing that is missing is support for one file audiobooks like m4b (which are now quite popular), large mp3 or aax (which are basically m4b, just encrypted with audible drm) or m4a (same as m4b just different extension).
Idea is to represent such file as a folder,, where containing files will be chapters in the file (or in case where there are no chapters meta just parts of same size, say 30 minutes).
This will require that chapters will be always served through ffmpeg remuxed or transcode to given chapter part only - start time till end time.
The key prerequisite is meta parsing library, that provides chapters meta ( need to look to my livavformat binding).
Best way to represent in API is to extend attributes for folder (to indicate that folder in actually multi chapters file) and for file (indicate that file is actually chapter, it's time offset in the file).

M4B files with AAC using "scalable configurations" cannot be played without transcoding

Without transcoding, just remuxing audio stream ffmpeg fails with

[adts @ 0x562cb448d440] Scalable configurations are not allowed in ADTS
Could not write header for output file #0 (incorrect codec parameters ?): Invalid data found when processing input

Also some information about similar problem is here https://rentry.co/n4ost
From there:

FWIW, I've got to the bottom of the problem with those specific files .. they're de-DRMed Audible AAX files, and for some reason those sampled at 44.1kHz have a bit flipped in the ESDS atom's Audio Specific Config section .. it's referred to as "dependsOnCoreCoder" in most OSS code. Flipping it back makes the file playable without transcoding.

So question is now - is it a problem of file - incorrectly prepared? Or can we somehow fix with some additional ffmpeg params?

Does not look as easy to fix - as for now WA is play them transcoded.

Option typo in main document

they can be enabled by --allow-soflinks program argument.

Should this be --allow-symlinks? The latter works, the former causes a server error.

iOS and OSX+Safari do not support opus+ogg audio format

Apple platforms do not support opus+ogg format, which is default in audioserve.
On OSX alternative browser(FF or Chrome) seems to work fine, but Safari is not.

I think Apple supports opus, but only in matroska container, which does not work well with on-flow transcoding (as it requires seek access to stream to update header).

I think we should provide alternative configuration for apple platforms - probably aac codec?
As I'm not using Apple, I'm just opening this issue if somebody has more experiences (and it would not be any problem to add additional transcoding configurations, provided it's supported by ffmpeg).

@paulmorabito - what transcodings did work for you on iOS?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.