#lang scribble/manual @(require (for-label racket/base racket/contract rkt-web-player rkt-web-player/player-agent rkt-web-player/users)) @title{RKT Web Player} @author{Hans van Dijkema} @defmodule[rkt-web-player] RKT Web Player combines local playback through @tt{racket-audio} with UPnP and Sonos discovery and playback through @tt{racket-audio-dlna}. The user interface is served to a web browser by Racket's web server. @defproc[(run-web-player [music-paths (listof (or/c path-string? (list/c string? path-string?)))] [#:allowed-agent-ids allowed-agent-ids (listof string?) null] [#:users users (listof (cons/c string? string?)) null] [#:trusted-proxies trusted-proxies (listof string?)] [#:session-seconds session-seconds exact-positive-integer? 604800] [#:listen-ip listen-ip string? "127.0.0.1"] [#:port port exact-positive-integer? 8080] [#:dlna-port dlna-port exact-positive-integer? 8734] [#:local-output? local-output? boolean? #t] [#:playlist-keystore playlist-keystore (or/c path-string? #f)] [#:launch-browser? launch-browser? boolean? #t]) any/c] { Treats every entry in @racket[music-paths] as a separate music library. A two-element list supplies an explicit display name and path. The @racket[allowed-agent-ids] list contains the 64-character application IDs that may register as polling playback agents. All agents are denied when it is empty. The function starts the web interface on @racket[listen-ip] and @racket[port]. Directory contents are browsed one level at a time. Metadata and recursive contents are only read when the user adds or plays a selected item. The @racket[dlna-port] is used to publish local audio files to a selected network renderer. Set @racket[local-output?] to @racket[#f] to omit the server's own @tt{racket-audio} output. When no other renderer is available, the player waits for discovery or playback-agent registration. Playlist tabs are atomically persisted in @racket[playlist-keystore]; @racket[#f] disables playlist persistence. Player resources are closed when the web server exits. The default is @tt{data/playlists.keystore} below the installed rkt-web-player collection. Each username owns an ordered GUID index and separate playlist values. It also owns an independent playback pipeline and a durable interface-language preference. Different outputs can play concurrently. Selecting an output that another user owns stops the previous backend and transfers that output. The web interface supports Dutch, English, German, French, Spanish, Italian, Swedish, Norwegian, Finnish and Icelandic; browser language is the initial default and a manual choice is stored per username. When @racket[users] is non-empty, every browser client must authenticate. Sessions have a sliding idle timeout; an active browser cookie is renewed once half of @racket[session-seconds] has elapsed. Playback-agent endpoints continue to use their separate application-ID allowlist. The default listen address only exposes the interface to the local computer. Use a LAN address deliberately if other devices should control the player. } @defmodule[rkt-web-player/users] @defproc[(make-password-hash [password string?]) string?] { Creates a salted Argon2id password hash suitable for a value in the INI @tt{[users]} section. Passwords must contain at least twelve characters. } @defproc[(password-hash-valid? [password string?] [encoded string?]) boolean?] { Checks a password against an encoded Argon2id hash. } @defmodule[rkt-web-player/player-agent] @defproc[(run-player-agent) any/c] { Starts the graphical polling playback agent. The agent keeps a generated 256-bit application identifier in a user-specific INI file, registers with the configured RKT Web Player server, downloads assigned tracks over HTTP, and plays them with @tt{racket-audio}. Its configured display name is authoritative and is followed by the server. Importing the module does not start the GUI; the function must be called explicitly. The GUI and optional tray support the same ten languages based on the operating-system language, with English as fallback. } @defproc[(run-player-agent-cli [#:server-url server-url (or/c string? #f) #f] [#:name name (or/c string? #f) #f] [#:config-file config-file (or/c path-string? #f) #f]) void?] { Starts a headless playback agent using the same audio and gapless-prefetch runtime as the GUI. Missing keyword values are read from the normal agent INI file. The procedure runs until interrupted. }