verbetering tray implementatie en prefetching

This commit is contained in:
2026-08-27 11:26:25 +02:00
parent a2dea77eb0
commit d535bb63d9
4 changed files with 228 additions and 209 deletions
+112 -25
View File
@@ -34,8 +34,7 @@
[reported-state #:mutable]
[commands #:mutable]
[next-command-id #:mutable]
[media-token #:mutable]
[media-file #:mutable]
media
[ended-counter #:mutable])
#:transparent)
@@ -106,6 +105,25 @@
(define (fresh-media-token)
(bytes->hex-string (crypto-random-bytes 32)))
(define (track-cache-key item)
(sha1
(open-input-string
(path->string (track-file item)))))
(define (agent-track-data item index token)
(hasheq 'playlistIndex index
'trackNumber (+ index 1)
'cacheKey (track-cache-key item)
'mediaToken token
'filename
(path->string
(or (file-name-from-path (track-file item))
(track-file item)))
'title (track-title item)
'artist (track-artist item)
'album (track-album item)
'duration (or (track-duration item) 'null)))
(define (enqueue-agent-command! value agent action [data (hasheq)])
(with-state-lock
value
@@ -353,25 +371,34 @@
((eq? kind 'local)
(audio-play! backend (track-file item)))
((eq? kind 'agent)
(let ((token (fresh-media-token)))
(let* ((token (fresh-media-token))
(data (agent-track-data item index token))
(following-index (next-index value 1))
(following-item
(and following-index
(not (= following-index index))
(list-ref (player-tracks value) following-index)))
(following-token
(and following-item (fresh-media-token)))
(following-data
(and following-item
(agent-track-data following-item
following-index
following-token))))
(with-state-lock
value
(λ ()
(set-playback-agent-media-token! backend token)
(set-playback-agent-media-file! backend (track-file item))))
(enqueue-agent-command!
value
backend
"play"
(hasheq 'mediaToken token
'filename
(path->string
(or (file-name-from-path (track-file item))
(track-file item)))
'title (track-title item)
'artist (track-artist item)
'album (track-album item)
'duration (or (track-duration item) 'null)))))
(hash-set! (playback-agent-media backend)
token
(track-file item))
(when following-item
(hash-set! (playback-agent-media backend)
following-token
(track-file following-item)))))
(enqueue-agent-command! value backend "play" data)
(when following-data
(enqueue-agent-command!
value backend "prefetch" following-data))))
(else
(dlna-player-play! backend (track-file item))))
(clear-error! value)))
@@ -405,11 +432,15 @@
(playback-agent-reported-state
(player-backend value))))
;; Keep the server's optimistic command state visible until the
;; agent acknowledges all queued work. Its report in the poll that
;; receives a command still describes the state before execution.
;; agent acknowledges playback-changing work. Prefetching does not
;; change playback state and may continue in the background.
(when (and (hash? reported)
(null? (playback-agent-commands
(player-backend value))))
(andmap
(λ (command)
(string=? (hash-ref command 'action "")
"prefetch"))
(playback-agent-commands
(player-backend value))))
(with-state-lock
value
(λ ()
@@ -1171,7 +1202,7 @@
(hasheq 'state "stopped"
'position 0
'volume 50)
'() 1 #f #f 0)))
'() 1 (make-hash) 0)))
(set-player-agents!
value
(append (player-agents value) (list agent)))
@@ -1228,6 +1259,15 @@
(when (hash? reported)
(set-playback-agent-reported-state! agent reported))
(when (exact-nonnegative-integer? ack)
(for ((command (in-list (playback-agent-commands agent)))
#:when (<= (hash-ref command 'id) ack))
(let* ((command-data
(hash-ref command 'data (hasheq)))
(media-token
(hash-ref command-data 'mediaToken #f)))
(when (string? media-token)
(hash-remove! (playback-agent-media agent)
media-token))))
(set-playback-agent-commands!
agent
(filter
@@ -1265,8 +1305,7 @@
(agent-by-id value app-id))))
(and agent
(string? token)
(equal? token (playback-agent-media-token agent))
(playback-agent-media-file agent))))))
(hash-ref (playback-agent-media agent) token #f))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Stop playback and release all player resources.
@@ -1435,6 +1474,54 @@
(player-renderers example-player)))
"Office laptop")
(define first-file (build-path root "Album" "01.flac"))
(define second-file (build-path root "Album" "02.flac"))
(call-with-output-file first-file void #:exists 'truncate/replace)
(call-with-output-file second-file void #:exists 'truncate/replace)
(set-player-tracks!
example-player
(list (track first-file "First" "Artist" "Album" 60 "audio/flac")
(track second-file "Second" "Artist" "Album" 60 "audio/flac")))
(player-command! example-player "play" (hasheq 'index 0))
(define play-poll
(player-agent-poll!
example-player
(hasheq 'appId test-agent-id
'ack first-command-id
'endedCounter 0
'state (hasheq 'state "starting" 'position 0))))
(define play-command (hash-ref play-poll 'command))
(check-equal? (hash-ref play-command 'action) "play")
(check-equal?
(hash-ref (hash-ref play-command 'data) 'trackNumber)
1)
(check-equal?
(player-agent-media
example-player
test-agent-id
(hash-ref (hash-ref play-command 'data) 'mediaToken))
first-file)
(define prefetch-poll
(player-agent-poll!
example-player
(hasheq 'appId test-agent-id
'ack (hash-ref play-command 'id)
'endedCounter 0
'state (hasheq 'state "playing" 'position 1))))
(define prefetch-command (hash-ref prefetch-poll 'command))
(check-equal? (hash-ref prefetch-command 'action) "prefetch")
(check-equal?
(hash-ref (hash-ref prefetch-command 'data) 'trackNumber)
2)
(check-equal?
(player-agent-media
example-player
test-agent-id
(hash-ref (hash-ref prefetch-command 'data) 'mediaToken))
second-file)
(check-exn exn:fail?
(λ ()
(player-command!