ini file support and cover art

This commit is contained in:
2026-08-27 13:33:34 +02:00
parent 744a2815f1
commit 84891adb89
13 changed files with 422 additions and 79 deletions
+81 -15
View File
@@ -19,6 +19,8 @@
player-agent-register!
player-agent-poll!
player-agent-media
player-track-artwork
exn:fail:agent-denied?
player-close!)
(sl-def-log web-player)
@@ -38,12 +40,16 @@
[ended-counter #:mutable])
#:transparent)
(struct exn:fail:agent-denied exn:fail ()
#:transparent)
(struct playlist-tab
(id [name #:mutable] [tracks #:mutable])
#:transparent)
(struct player
(libraries
allowed-agent-ids
[agents #:mutable]
[current-library-id #:mutable]
[browser-path #:mutable]
@@ -103,6 +109,25 @@
(and (string? value)
(regexp-match? #px"^[0-9a-fA-F]{64}$" value)))
(define (normal-agent-id value)
(and (valid-agent-id? value)
(string-downcase value)))
(define (authorized-agent-id? value app-id)
(and (normal-agent-id app-id)
(member (normal-agent-id app-id)
(player-allowed-agent-ids value))
#t))
(define (require-authorized-agent! value app-id)
(unless (authorized-agent-id? value app-id)
(raise
(exn:fail:agent-denied
(format
"Playback agent ~a is niet toegestaan; voeg het applicatie-ID eerst toe aan [playback-agents] in de server-INI"
(or app-id "(ontbreekt)"))
(current-continuation-marks)))))
(define (fresh-media-token)
(bytes->hex-string (crypto-random-bytes 32)))
@@ -736,6 +761,7 @@
'album (track-album item)
'duration (or (track-duration item) 'null)
'mimeType (track-mime-type item)
'artworkId (track-cache-key item)
'source (path->string
(or (file-name-from-path (track-file item))
(track-file item)))))
@@ -1011,11 +1037,21 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Create a player for lazily browsed music libraries.
; pre : Libraries is a list of music-library values; DLNA port is positive.
; pre : Libraries are valid; allowed agent IDs are 256-bit hex strings.
; post : Only the selected root directory has been listed; no backend exists.
; result : A player that initially selects local playback.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (make-player libraries #:dlna-port [dlna-port 8734])
(define (make-player libraries
#:allowed-agent-ids [allowed-agent-ids '()]
#:dlna-port [dlna-port 8734])
(define normalized-agent-ids
(for/list ((app-id (in-list allowed-agent-ids)))
(unless (valid-agent-id? app-id)
(raise-argument-error
'make-player
"64-character hexadecimal playback agent id"
app-id))
(string-downcase app-id)))
(let* ((library (and (pair? libraries) (car libraries)))
(browser-entries
(if library
@@ -1023,6 +1059,7 @@
'()))
(tab (playlist-tab "default" "Default" '())))
(player libraries
(remove-duplicates normalized-agent-ids string=?)
'()
(and library (music-library-id library))
'()
@@ -1184,12 +1221,13 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Register or refresh one polling playback agent.
; pre : Data contains a 256-bit hexadecimal application id.
; pre : Data contains an allowlisted 256-bit hexadecimal application id.
; post : The agent is available as a renderer under its advertised name.
; result : Agent configuration for the polling client.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (player-agent-register! value data)
(let* ((app-id (json-string data 'appId #f))
(let* ((supplied-app-id (json-string data 'appId #f))
(app-id (normal-agent-id supplied-app-id))
(suggested-name
(string-trim
(or (json-string data 'name #f)
@@ -1198,11 +1236,12 @@
(if (string=? suggested-name "")
"RKT playback agent"
suggested-name)))
(unless (valid-agent-id? app-id)
(unless app-id
(raise-arguments-error
'player-agent-register!
"appId must contain exactly 64 hexadecimal characters"
"appId" app-id))
"appId" supplied-app-id))
(require-authorized-agent! value app-id)
(with-state-lock
value
(λ ()
@@ -1247,13 +1286,15 @@
; result : Poll response containing the current agent name and optional command.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (player-agent-poll! value data)
(let ((app-id (json-string data 'appId #f))
(let* ((supplied-app-id (json-string data 'appId #f))
(app-id (normal-agent-id supplied-app-id))
(name (json-string data 'name #f)))
(unless (valid-agent-id? app-id)
(unless app-id
(raise-arguments-error
'player-agent-poll!
"appId must contain exactly 64 hexadecimal characters"
"appId" app-id))
"appId" supplied-app-id))
(require-authorized-agent! value app-id)
(call-with-semaphore
(player-command-lock value)
(λ ()
@@ -1324,12 +1365,30 @@
(with-state-lock
value
(λ ()
(let ((agent (and (valid-agent-id? app-id)
(agent-by-id value app-id))))
(let* ((normalized (normal-agent-id app-id))
(agent (and normalized
(authorized-agent-id? value normalized)
(agent-by-id value normalized))))
(and agent
(string? token)
(hash-ref (playback-agent-media agent) token #f))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Resolve an opaque playlist artwork id.
; pre : Artwork id came from player-state->jsexpr.
; post : Player state remains unchanged.
; result : Artwork bytes and MIME type, or #f when unavailable.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (player-track-artwork value artwork-id)
(let ((item
(with-state-lock
value
(λ ()
(findf (λ (candidate)
(string=? (track-cache-key candidate) artwork-id))
(player-tracks value))))))
(and item (track-artwork item))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Stop playback and release all player resources.
; pre : Value was created with make-player.
@@ -1358,7 +1417,10 @@
(λ ()
(make-directory (build-path root "Album"))
(let* ((libraries (make-music-libraries (list root)))
(example-player (make-player libraries))
(test-agent-id (make-string 64 #\a))
(example-player
(make-player libraries
#:allowed-agent-ids (list test-agent-id)))
(initial-state
(player-state->jsexpr example-player)))
(check-equal? (hash-ref initial-state 'state) "stopped")
@@ -1423,9 +1485,6 @@
(check-equal? (length (hash-ref deleted-state 'tabs)) 1)
(define test-agent-id
(make-string 64 #\a))
(define registration
(player-agent-register!
example-player
@@ -1433,6 +1492,13 @@
'name "Test laptop")))
(check-equal? (hash-ref registration 'name) "Test laptop")
(check-exn
exn:fail:agent-denied?
(λ ()
(player-agent-register!
example-player
(hasheq 'appId (make-string 64 #\b)
'name "Unknown laptop"))))
(define agent-renderer-state
(player-command!