Initial import
This commit is contained in:
@@ -1,3 +1,165 @@
|
||||
# racket-dlna
|
||||
# Racket UPnP module
|
||||
|
||||
DLNA implementation for racket
|
||||
This directory contains a small, layered UPnP control-point implementation.
|
||||
The public interfaces deliberately hide SSDP, XML, SCPD and SOAP details unless
|
||||
the generic service escape hatch is used.
|
||||
|
||||
## Module layout
|
||||
|
||||
```text
|
||||
upnp/main.rkt generic public interface
|
||||
upnp/media-renderer.rkt high-level renderer operations
|
||||
upnp/media-server.rkt high-level MediaServer browser
|
||||
upnp/services/av-transport.rkt playback transport
|
||||
upnp/services/rendering-control.rkt volume and mute
|
||||
upnp/services/connection-manager.rkt protocol and connection information
|
||||
upnp/services/content-directory.rkt media-server browse and search
|
||||
upnp/ssdp.rkt low-level SSDP discovery
|
||||
upnp/device.rkt device-description parsing
|
||||
upnp/service.rkt SCPD inspection and SOAP calls
|
||||
```
|
||||
|
||||
The collection can be required as `upnp` when the package root is on Racket's
|
||||
collection path.
|
||||
|
||||
## Generic device discovery
|
||||
|
||||
```racket
|
||||
#lang racket/base
|
||||
|
||||
(require upnp)
|
||||
|
||||
(define devices
|
||||
(query-upnp-devices '(media-renderer scanner)
|
||||
#:interface "10.7.3.118"
|
||||
#:dns? #t))
|
||||
|
||||
(for ([device (in-list devices)])
|
||||
(printf "~a: ~a\n"
|
||||
(upnp-device-kind device)
|
||||
(upnp-device-name device)))
|
||||
```
|
||||
|
||||
Known friendly device kinds are returned by:
|
||||
|
||||
```racket
|
||||
(upnp-device-kinds)
|
||||
```
|
||||
|
||||
Without a kind, all described UPnP devices are returned:
|
||||
|
||||
```racket
|
||||
(query-upnp-devices)
|
||||
```
|
||||
|
||||
Unknown vendor-specific device types are retained and classified as
|
||||
`'unknown`; their original URN remains available through `upnp-device-type`.
|
||||
|
||||
## Service inspection
|
||||
|
||||
```racket
|
||||
(define renderer (car (query-upnp-devices 'media-renderer)))
|
||||
|
||||
(for ([service (in-list (upnp-device-services renderer))])
|
||||
(printf "~a: ~a\n"
|
||||
(upnp-service-kind service)
|
||||
(upnp-service-actions service)))
|
||||
```
|
||||
|
||||
`upnp-service-call` is the generic escape hatch for standard services that do
|
||||
not yet have a typed module and for manufacturer-specific services.
|
||||
|
||||
## One module per known service
|
||||
|
||||
```racket
|
||||
(require upnp/services/av-transport
|
||||
upnp/services/rendering-control)
|
||||
|
||||
(define transport (device-av-transport renderer))
|
||||
(define rendering (device-rendering-control renderer))
|
||||
|
||||
(av-transport-set-uri!
|
||||
transport
|
||||
"http://10.7.3.118:8080/media/test.flac")
|
||||
(av-transport-play! transport)
|
||||
(rendering-control-set-volume! rendering 30)
|
||||
```
|
||||
|
||||
The currently implemented typed service modules are:
|
||||
|
||||
- `upnp/services/av-transport`
|
||||
- `upnp/services/rendering-control`
|
||||
- `upnp/services/connection-manager`
|
||||
- `upnp/services/content-directory`
|
||||
|
||||
A typed service value is still the generic `upnp-service` value. No extra
|
||||
wrapper objects or public constructors are needed.
|
||||
|
||||
|
||||
## Media-server browser
|
||||
|
||||
The higher media-server layer parses ContentDirectory DIDL-Lite into ordinary
|
||||
containers, items and resources:
|
||||
|
||||
```racket
|
||||
(require upnp/media-server)
|
||||
|
||||
(define synology
|
||||
(car (query-media-servers #:interface "10.7.3.118")))
|
||||
|
||||
(define root (media-server-root synology))
|
||||
(define music
|
||||
(findf (lambda (entry)
|
||||
(and (media-container? entry)
|
||||
(string=? (media-entry-title entry) "Muziek")))
|
||||
root))
|
||||
|
||||
(define entries (media-container-children synology music))
|
||||
|
||||
(for ([entry (in-list entries)])
|
||||
(printf "~a: ~a\n"
|
||||
(if (media-container? entry) 'container 'item)
|
||||
(media-entry-title entry)))
|
||||
```
|
||||
|
||||
A media item can advertise several resources. Their URLs are available with
|
||||
`media-item-resources` and `media-resource-uri`. Containers are followed one
|
||||
level at a time; the published hierarchy can be a logical artist/album/genre
|
||||
view rather than the physical filesystem.
|
||||
|
||||
## Media-renderer convenience layer
|
||||
|
||||
Normal playback code need not mention AVTransport or SOAP:
|
||||
|
||||
```racket
|
||||
(require upnp
|
||||
upnp/media-renderer)
|
||||
|
||||
(define denon
|
||||
(car (query-media-renderers
|
||||
#:interface "10.7.3.118"
|
||||
#:dns? #t)))
|
||||
|
||||
(media-renderer-play-uri!
|
||||
denon
|
||||
"http://10.7.3.118:8080/media/test.flac")
|
||||
|
||||
(media-renderer-seek! denon 120)
|
||||
(media-renderer-set-volume! denon 30)
|
||||
```
|
||||
|
||||
`query-media-renderers` returns all UPnP MediaRenderers by default. Add
|
||||
`#:dlna-only? #t` to retain only devices advertising `X_DLNADOC`.
|
||||
|
||||
A later HTTP media-serving module can implement `play-file!` by publishing a
|
||||
local file through Racket's web-server framework and passing the resulting URL
|
||||
to `media-renderer-play-uri!`.
|
||||
|
||||
## Documentation and tests
|
||||
|
||||
The Scribble manual is in `upnp/scribblings/upnp.scrbl`.
|
||||
|
||||
The test file `upnp/tests/upnp-test.rkt` uses a local mock HTTP/SOAP server and
|
||||
covers service discovery from SCPD, SOAP calls, SOAP faults, AVTransport,
|
||||
RenderingControl, ConnectionManager, ContentDirectory and DIDL-Lite media
|
||||
server browsing.
|
||||
|
||||
Reference in New Issue
Block a user