This commit is contained in:
2026-07-31 10:23:23 +02:00
parent ad0d676853
commit 9daf7e7e3d
7 changed files with 126 additions and 3 deletions
+24
View File
@@ -29,6 +29,11 @@
(define (didl-lite-audio-item uri (define (didl-lite-audio-item uri
#:protocol-info protocol-info #:protocol-info protocol-info
#:title [title "Media"] #:title [title "Media"]
#:artist [artist #f]
#:album [album #f]
#:genre [genre #f]
#:year [year #f]
#:track [track #f]
#:duration [duration #f] #:duration [duration #f]
#:size [size #f] #:size [size #f]
#:id [id "0"] #:id [id "0"]
@@ -38,6 +43,18 @@
(check-string 'didl-lite-audio-item title) (check-string 'didl-lite-audio-item title)
(check-string 'didl-lite-audio-item id) (check-string 'didl-lite-audio-item id)
(check-string 'didl-lite-audio-item parent-id) (check-string 'didl-lite-audio-item parent-id)
(for ([value (in-list (list artist album genre))])
(unless (or (not value) (string? value))
(raise-argument-error
'didl-lite-audio-item
"(or/c #f string?)"
value)))
(for ([value (in-list (list year track))])
(unless (or (not value) (exact-nonnegative-integer? value))
(raise-argument-error
'didl-lite-audio-item
"(or/c #f exact-nonnegative-integer?)"
value)))
(unless (or (not size) (unless (or (not size)
(exact-nonnegative-integer? size)) (exact-nonnegative-integer? size))
(raise-argument-error (raise-argument-error
@@ -55,6 +72,13 @@
(restricted "1")) (restricted "1"))
(dc:title () ,title) (dc:title () ,title)
(upnp:class () "object.item.audioItem.musicTrack") (upnp:class () "object.item.audioItem.musicTrack")
,@(if artist `((upnp:artist () ,artist)) '())
,@(if album `((upnp:album () ,album)) '())
,@(if genre `((upnp:genre () ,genre)) '())
,@(if year `((dc:date () ,(number->string year))) '())
,@(if track
`((upnp:originalTrackNumber () ,(number->string track)))
'())
(res (res
((protocolInfo ,protocol-info) ((protocolInfo ,protocol-info)
,@(if duration ,@(if duration
+3 -3
View File
@@ -1,7 +1,7 @@
#lang info #lang info
(define pkg-authors '(hnmdijkema)) (define pkg-authors '(hnmdijkema))
(define version "0.1.2") (define version "0.1.3")
(define license 'MIT) (define license 'MIT)
(define collection "racket-upnp") (define collection "racket-upnp")
(define pkg-desc "racket-upnp - UpnP and DLNA for racket") (define pkg-desc "racket-upnp - UpnP and DLNA for racket")
@@ -10,9 +10,9 @@
'("base" '("base"
"net-lib" "net-lib"
"racket-mimetypes" "racket-mimetypes"
"web-server-lib")) ("web-server-lib" #:version "1.10")))
(define build-deps '("racket-doc" "scribble-lib" "rackunit-lib")) (define build-deps '("racket-doc" "scribble-lib"))
(define scribblings (define scribblings
'(("scribblings/racket-upnp.scrbl" (multi-page) (library)))) '(("scribblings/racket-upnp.scrbl" (multi-page) (library))))
+10
View File
@@ -269,6 +269,11 @@
(define (media-file-server-didl-lite server url (define (media-file-server-didl-lite server url
#:title [title #f] #:title [title #f]
#:artist [artist #f]
#:album [album #f]
#:genre [genre #f]
#:year [year #f]
#:track [track #f]
#:duration [duration #f] #:duration [duration #f]
#:id [id "0"] #:id [id "0"]
#:parent-id [parent-id "0"]) #:parent-id [parent-id "0"])
@@ -312,6 +317,11 @@
#:title #:title
(or title (or title
(path->string (file-name-from-path path))) (path->string (file-name-from-path path)))
#:artist artist
#:album album
#:genre genre
#:year year
#:track track
#:duration duration #:duration duration
#:size (file-size path) #:size (file-size path)
#:id id #:id id
+7
View File
@@ -17,6 +17,11 @@ type, duration, and byte-range capability.
[uri string?] [uri string?]
[#:protocol-info protocol-info string?] [#:protocol-info protocol-info string?]
[#:title title string? "Media"] [#:title title string? "Media"]
[#:artist artist (or/c #f string?) #f]
[#:album album (or/c #f string?) #f]
[#:genre genre (or/c #f string?) #f]
[#:year year (or/c #f exact-nonnegative-integer?) #f]
[#:track track (or/c #f exact-nonnegative-integer?) #f]
[#:duration duration (or/c #f nonnegative-real?) #f] [#:duration duration (or/c #f nonnegative-real?) #f]
[#:size size (or/c #f exact-nonnegative-integer?) #f] [#:size size (or/c #f exact-nonnegative-integer?) #f]
[#:id id string? "0"] [#:id id string? "0"]
@@ -35,6 +40,8 @@ For example:
#:protocol-info #:protocol-info
"http-get:*:audio/flac:DLNA.ORG_OP=01;DLNA.ORG_CI=0" "http-get:*:audio/flac:DLNA.ORG_OP=01;DLNA.ORG_CI=0"
#:title "Test track" #:title "Test track"
#:artist "Test artist"
#:album "Test album"
#:duration 639 #:duration 639
#:size 50357462) #:size 50357462)
] ]
+5
View File
@@ -82,6 +82,11 @@ finish.
[server media-file-server?] [server media-file-server?]
[url string?] [url string?]
[#:title title (or/c #f string?) #f] [#:title title (or/c #f string?) #f]
[#:artist artist (or/c #f string?) #f]
[#:album album (or/c #f string?) #f]
[#:genre genre (or/c #f string?) #f]
[#:year year (or/c #f exact-nonnegative-integer?) #f]
[#:track track (or/c #f exact-nonnegative-integer?) #f]
[#:duration duration (or/c #f nonnegative-real?) #f] [#:duration duration (or/c #f nonnegative-real?) #f]
[#:id id string? "0"] [#:id id string? "0"]
[#:parent-id parent-id string? "0"]) [#:parent-id parent-id string? "0"])
+10
View File
@@ -12,6 +12,11 @@
#:protocol-info #:protocol-info
"http-get:*:audio/flac:DLNA.ORG_OP=01;DLNA.ORG_CI=0" "http-get:*:audio/flac:DLNA.ORG_OP=01;DLNA.ORG_CI=0"
#:title "Rock & Roll <live>" #:title "Rock & Roll <live>"
#:artist "Test Artist"
#:album "Test Album"
#:genre "Alternative"
#:year 2026
#:track 7
#:duration 639.9 #:duration 639.9
#:size 50357462)) #:size 50357462))
@@ -31,6 +36,11 @@
"Rock & Roll <live>") "Rock & Roll <live>")
(check-equal? (xexpr-child-text item "class") (check-equal? (xexpr-child-text item "class")
"object.item.audioItem.musicTrack") "object.item.audioItem.musicTrack")
(check-equal? (xexpr-child-text item "artist") "Test Artist")
(check-equal? (xexpr-child-text item "album") "Test Album")
(check-equal? (xexpr-child-text item "genre") "Alternative")
(check-equal? (xexpr-child-text item "date") "2026")
(check-equal? (xexpr-child-text item "originalTrackNumber") "7")
(check-equal? (xexpr-text resource) (check-equal? (xexpr-text resource)
"http://example.test/audio?a=1&b=2") "http://example.test/audio?a=1&b=2")
(check-equal? (xexpr-attribute resource 'protocolInfo) (check-equal? (xexpr-attribute resource 'protocolInfo)
+67
View File
@@ -0,0 +1,67 @@
#lang racket/base
(require rackunit
racket/file
racket/port
racket/tcp
"../main.rkt")
(define (available-port)
(let ([listener (tcp-listen 0 4 #t "127.0.0.1")])
(let-values ([(_local-host port _remote-host _remote-port)
(tcp-addresses listener #t)])
(tcp-close listener)
port)))
(define (http-request port method #:range [range #f])
(let-values ([(in out) (tcp-connect "127.0.0.1" port)])
(fprintf out "~a /media/track.flac HTTP/1.1\r\n" method)
(fprintf out "Host: 127.0.0.1:~a\r\n" port)
(when range
(fprintf out "Range: ~a\r\n" range))
(fprintf out "Connection: close\r\n\r\n")
(flush-output out)
(close-output-port out)
(let ([response (port->bytes in)])
(close-input-port in)
response)))
(define (response-body response)
(let ([match (regexp-match-positions #rx#"\r\n\r\n" response)])
(if match
(subbytes response (cdar match))
#"")))
(define port (available-port))
(define file (make-temporary-file "racket-upnp-range-~a.flac"))
(define server #f)
(dynamic-wind
(lambda ()
(call-with-output-file
file
#:exists 'truncate
(lambda (out)
(write-bytes #"0123456789" out)))
(set! server
(start-media-file-server
(format "http://127.0.0.1:~a/media/" port)
#:listen-ip "127.0.0.1"))
(media-file-server-publish! server file "track.flac"))
(lambda ()
(let ([head (http-request port "HEAD")]
[range (http-request port "GET" #:range "bytes=2-5")])
(check-regexp-match #rx#"HTTP/1[.]1 200" head)
(check-regexp-match #rx#"(?i:Accept-Ranges): bytes" head)
(check-regexp-match
#rx#"(?i:contentFeatures[.]dlna[.]org): DLNA[.]ORG_OP=01;DLNA[.]ORG_CI=0"
head)
(check-equal? (response-body head) #"")
(check-regexp-match #rx#"HTTP/1[.]1 206" range)
(check-regexp-match #rx#"(?i:Content-Range): bytes 2-5/10" range)
(check-equal? (response-body range) #"2345")))
(lambda ()
(when server
(media-file-server-stop! server))
(when (file-exists? file)
(delete-file file))))