#lang racket/base (require racket/cmdline racket/contract racket/list racket/mpair racket/string simple-ini simple-log "private/library.rkt" "private/player.rkt" "private/server.rkt" "private/users.rkt") (provide run-web-player) (sl-def-log rkt-web-player) (define library-spec/c (or/c path-string? (list/c string? path-string?))) (define (ini-section-key-values config section-name) (let ((section (assoc section-name (mcdr config)))) (if section (for/list ((line (in-list (cdr section))) #:when (and (pair? line) (eq? (car line) 'keyval))) (cons (symbol->string (cadr line)) (caddr line))) '()))) (define (configuration-list value defaults) (cond ((list? value) (map (lambda (item) (format "~a" item)) value)) ((and (string? value) (not (string=? (string-trim value) ""))) (map string-trim (string-split value ";"))) (else defaults))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Provided functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Start the audio player and its web interface. ; pre : Ports are valid; every library spec names an existing directory. ; post : Libraries are browsed lazily; player resources close with the server. ; result : The result returned by serve/servlet. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define/contract (run-web-player music-paths #:allowed-agent-ids [allowed-agent-ids '()] #:users [users '()] #:local-networks [local-networks '("127.0.0.0/8" "::1/128" "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16")] #:trusted-proxies [trusted-proxies '("127.0.0.0/8" "::1/128")] #:session-seconds [session-seconds 43200] #:listen-ip [listen-ip "127.0.0.1"] #:port [port 8080] #:dlna-port [dlna-port 8734] #:launch-browser? [launch-browser? #t]) (->* ((listof library-spec/c)) (#:allowed-agent-ids (listof string?) #:users (listof (cons/c string? string?)) #:local-networks (listof string?) #:trusted-proxies (listof string?) #:session-seconds exact-positive-integer? #:listen-ip string? #:port exact-positive-integer? #:dlna-port exact-positive-integer? #:launch-browser? boolean?) any) (let* ((libraries (make-music-libraries music-paths)) (player (make-player libraries #:allowed-agent-ids allowed-agent-ids #:dlna-port dlna-port)) (auth-manager (make-auth-manager users #:local-networks local-networks #:trusted-proxies trusted-proxies #:session-seconds session-seconds))) (info-rkt-web-player "Starting with ~a music library/libraries on http://~a:~a/" (length libraries) listen-ip port) (dynamic-wind void (λ () (serve-player player #:auth-manager auth-manager #:listen-ip listen-ip #:port port #:launch-browser? launch-browser?)) (λ () (player-close! player))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Command line ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (module+ main (define listen-ip #f) (define port #f) (define dlna-port #f) (define launch-browser? #t) (define config-file #f) (define music-paths (command-line #:program "rkt-web-player" #:once-each [("--listen-ip") address "Address on which the web server listens" (set! listen-ip address)] [("--port") value "Web server port" (set! port (string->number value))] [("--dlna-port") value "Port used to publish local audio to DLNA renderers" (set! dlna-port (string->number value))] [("--config") path "Read defaults from an INI file" (set! config-file path)] [("--no-browser") "Do not open the web interface automatically" (set! launch-browser? #f)] #:args paths paths)) (define config (if config-file (file->ini config-file) (make-ini))) (define configured-paths-value (ini-get config 'library 'paths "")) (define configured-paths (cond ((list? configured-paths-value) configured-paths-value) ((and (string? configured-paths-value) (not (string=? (string-trim configured-paths-value) ""))) (map string-trim (string-split configured-paths-value ";"))) (else '()))) (define configured-libraries (for/list ((entry (in-list (ini-section-key-values config 'libraries))) #:when (path-string? (cdr entry))) (list (car entry) (cdr entry)))) (define allowed-agent-ids (for/list ((entry (in-list (ini-section-key-values config 'playback-agents))) #:when (not (eq? (cdr entry) #f))) (car entry))) (define configured-users (for/list ((entry (in-list (ini-section-key-values config 'users))) #:when (string? (cdr entry))) entry)) (define local-networks (configuration-list (ini-get config 'authentication 'local-networks "") '("127.0.0.0/8" "::1/128" "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16"))) (define trusted-proxies (configuration-list (ini-get config 'authentication 'trusted-proxies "") '("127.0.0.0/8" "::1/128"))) (define session-seconds (ini-get config 'authentication 'session-seconds 43200)) (define all-libraries (append configured-libraries music-paths configured-paths)) (sl-log-to-display) (run-web-player all-libraries #:allowed-agent-ids allowed-agent-ids #:users configured-users #:local-networks local-networks #:trusted-proxies trusted-proxies #:session-seconds session-seconds #:listen-ip (or listen-ip (ini-get config 'server 'listen-ip "127.0.0.1")) #:port (or port (ini-get config 'server 'port 8080)) #:dlna-port (or dlna-port (ini-get config 'player 'dlna-port 8734)) #:launch-browser? launch-browser?))