Media file server
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
#lang racket/base
|
||||||
|
|
||||||
|
(require racket-upnp)
|
||||||
|
|
||||||
|
(define files
|
||||||
|
(start-media-file-server
|
||||||
|
"http://10.7.3.118:8080/media/"
|
||||||
|
#:listen-ip "10.7.3.118"))
|
||||||
|
|
||||||
|
(define current-uri
|
||||||
|
(media-file-server-publish!
|
||||||
|
files
|
||||||
|
"C:/muziek/current.flac"
|
||||||
|
"current.flac"))
|
||||||
|
|
||||||
|
(define next-uri
|
||||||
|
(media-file-server-publish!
|
||||||
|
files
|
||||||
|
"C:/muziek/next.flac"
|
||||||
|
"next.flac"))
|
||||||
|
|
||||||
|
(define renderer
|
||||||
|
(get-media-renderer "Denon"))
|
||||||
|
|
||||||
|
(when renderer
|
||||||
|
(media-renderer-play-uri!
|
||||||
|
renderer
|
||||||
|
current-uri
|
||||||
|
#:next-uri next-uri))
|
||||||
|
|
||||||
|
;; Call this after playback no longer needs the URLs:
|
||||||
|
;;
|
||||||
|
;; (media-file-server-unpublish! files current-uri)
|
||||||
|
;; (media-file-server-unpublish! files next-uri)
|
||||||
|
;; (media-file-server-stop! files)
|
||||||
@@ -1,23 +1,17 @@
|
|||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
;; Main public interface for racket-upnp.
|
;; Main public interface for racket-upnp.
|
||||||
;;
|
|
||||||
;; This module provides:
|
|
||||||
;; - generic UPnP device discovery and service inspection;
|
|
||||||
;; - the high-level media-renderer interface;
|
|
||||||
;; - the high-level media-server browser interface.
|
|
||||||
;;
|
|
||||||
;; Service-specific interfaces remain available through:
|
|
||||||
;; racket-upnp/services/<service>
|
|
||||||
|
|
||||||
(require "query.rkt"
|
(require "query.rkt"
|
||||||
"service.rkt"
|
"service.rkt"
|
||||||
"media-renderer.rkt"
|
"media-renderer.rkt"
|
||||||
"media-server.rkt")
|
"media-server.rkt"
|
||||||
|
"media-file-server.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
(all-from-out
|
(all-from-out
|
||||||
"query.rkt"
|
"query.rkt"
|
||||||
"service.rkt"
|
"service.rkt"
|
||||||
"media-renderer.rkt"
|
"media-renderer.rkt"
|
||||||
"media-server.rkt"))
|
"media-server.rkt"
|
||||||
|
"media-file-server.rkt"))
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require
|
||||||
|
(for-label
|
||||||
|
racket/base
|
||||||
|
(file "../media-file-server.rkt")))
|
||||||
|
|
||||||
|
@title{Publishing Media Files}
|
||||||
|
|
||||||
|
@defmodule[(file "../media-file-server.rkt")]
|
||||||
|
|
||||||
|
This module publishes local files through a small Racket web server. The
|
||||||
|
underlying Racket file dispatcher streams files and supports both @tt{HEAD}
|
||||||
|
requests and HTTP byte ranges. Byte ranges are important for media-renderer
|
||||||
|
probing and seeking.
|
||||||
|
|
||||||
|
@defproc[(start-media-file-server
|
||||||
|
[url string?]
|
||||||
|
[#:listen-ip listen-ip (or/c #f string?) #f])
|
||||||
|
media-file-server?]{
|
||||||
|
|
||||||
|
Starts a server for the HTTP origin and path prefix in @racket[url].
|
||||||
|
The URL must be an absolute @tt{http://} URL. A trailing slash is added when
|
||||||
|
needed.
|
||||||
|
|
||||||
|
When @racket[listen-ip] is @racket[#f], the server listens on all local
|
||||||
|
interfaces. Supply a local address, such as @tt{10.7.3.118}, to restrict the
|
||||||
|
listener to that interface.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-file-server-url
|
||||||
|
[server media-file-server?])
|
||||||
|
string?]{
|
||||||
|
|
||||||
|
Returns the normalized base URL of @racket[server].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-file-server-publish!
|
||||||
|
[server media-file-server?]
|
||||||
|
[file path-string?]
|
||||||
|
[url string?]
|
||||||
|
[#:mime-type mime-type (or/c #f bytes? string?) #f])
|
||||||
|
string?]{
|
||||||
|
|
||||||
|
Publishes @racket[file] at @racket[url] and returns the absolute URL.
|
||||||
|
|
||||||
|
The URL may be relative to the server URL:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(define uri
|
||||||
|
(media-file-server-publish!
|
||||||
|
files
|
||||||
|
"C:/muziek/test.flac"
|
||||||
|
"test.flac"))
|
||||||
|
]
|
||||||
|
|
||||||
|
It may also be the complete URL, provided that its origin and path prefix match
|
||||||
|
the server:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(media-file-server-publish!
|
||||||
|
files
|
||||||
|
"C:/muziek/test.flac"
|
||||||
|
"http://10.7.3.118:8080/media/test.flac")
|
||||||
|
]
|
||||||
|
|
||||||
|
The MIME type is inferred for common audio, video, and image extensions.
|
||||||
|
Use @racket[#:mime-type] to override it.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-file-server-unpublish!
|
||||||
|
[server media-file-server?]
|
||||||
|
[url string?])
|
||||||
|
void?]{
|
||||||
|
|
||||||
|
Removes the URL mapping. Requests that are already in progress may still
|
||||||
|
finish.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-file-server-stop!
|
||||||
|
[server media-file-server?])
|
||||||
|
void?]{
|
||||||
|
|
||||||
|
Stops the web server. Repeated calls have no effect.
|
||||||
|
}
|
||||||
|
|
||||||
|
@section{Playing local files}
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(define files
|
||||||
|
(start-media-file-server
|
||||||
|
"http://10.7.3.118:8080/media/"
|
||||||
|
#:listen-ip "10.7.3.118"))
|
||||||
|
|
||||||
|
(define first-uri
|
||||||
|
(media-file-server-publish!
|
||||||
|
files
|
||||||
|
"C:/muziek/first.flac"
|
||||||
|
"first.flac"))
|
||||||
|
|
||||||
|
(define second-uri
|
||||||
|
(media-file-server-publish!
|
||||||
|
files
|
||||||
|
"C:/muziek/second.flac"
|
||||||
|
"second.flac"))
|
||||||
|
|
||||||
|
(media-renderer-play-uri!
|
||||||
|
renderer
|
||||||
|
first-uri
|
||||||
|
#:next-uri second-uri)
|
||||||
|
]
|
||||||
|
|
||||||
|
Keep the URLs published until the renderer has finished reading them.
|
||||||
@@ -1,51 +1,114 @@
|
|||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@(require (for-label racket/base
|
@(require
|
||||||
racket/contract
|
(for-label
|
||||||
(file "../main.rkt")
|
racket/base
|
||||||
(file "../media-renderer.rkt")
|
(file "../media-renderer.rkt")))
|
||||||
(file "../services/av-transport.rkt")))
|
|
||||||
|
|
||||||
@title{Media Renderer}
|
@title{Media Renderers}
|
||||||
|
|
||||||
@defmodule[@racketmodname[racket-upnp/media-renderer] #:module-paths ((file "../media-renderer.rkt"))]
|
@defmodule[(file "../media-renderer.rkt")]
|
||||||
|
|
||||||
This module hides AVTransport and RenderingControl for common playback code.
|
This module provides a device-oriented interface for UPnP and DLNA media
|
||||||
The returned renderer remains an @racket[upnp-device?], so generic service
|
renderers. It hides the AVTransport and RenderingControl services used for
|
||||||
inspection is still available.
|
common playback and volume operations.
|
||||||
|
|
||||||
@defproc[(query-media-renderers [#:interface interface (or/c #f string?) #f]
|
@defproc[(query-media-renderers
|
||||||
|
[#:interface interface (or/c #f string?) #f]
|
||||||
[#:dns? dns? boolean? #f]
|
[#:dns? dns? boolean? #f]
|
||||||
[#:dlna-only? dlna-only? boolean? #f])
|
[#:dlna-only? dlna-only? boolean? #f])
|
||||||
(listof upnp-device?)]{
|
(listof media-renderer?)]{
|
||||||
Discovers UPnP MediaRenderers. When @racket[dlna-only?] is true, only devices
|
|
||||||
advertising an @tt{X_DLNADOC} value are returned.
|
Discovers media renderers. When @racket[dlna-only?] is true, only devices that
|
||||||
|
advertise a DLNA document identifier are returned.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(media-renderer? [value any/c]) boolean?]{Recognises a MediaRenderer device.}
|
@defproc[(get-media-renderer [filter-name string?])
|
||||||
@defproc[(media-renderer-dlna? [renderer media-renderer?]) boolean?]{Reports whether the device description advertises a DLNA document identifier.}
|
(or/c #f media-renderer?)]{
|
||||||
@defproc[(media-renderer-play-uri! [renderer media-renderer?]
|
|
||||||
|
Returns the first discovered renderer whose name contains
|
||||||
|
@racket[filter-name], ignoring case, or @racket[#f] when no renderer matches.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-renderer? [value any/c]) boolean?]
|
||||||
|
@defproc[(media-renderer-name [renderer media-renderer?]) string?]
|
||||||
|
@defproc[(media-renderer-address [renderer media-renderer?]) string?]
|
||||||
|
@defproc[(media-renderer-manufacturer [renderer media-renderer?])
|
||||||
|
(or/c #f string?)]
|
||||||
|
@defproc[(media-renderer-model [renderer media-renderer?])
|
||||||
|
(or/c #f string?)]
|
||||||
|
@defproc[(media-renderer-dlna? [renderer media-renderer?]) boolean?]
|
||||||
|
|
||||||
|
@section{Playback}
|
||||||
|
|
||||||
|
@defproc[(media-renderer-play-uri!
|
||||||
|
[renderer media-renderer?]
|
||||||
[uri string?]
|
[uri string?]
|
||||||
[#:metadata metadata string? ""]) void?]{Sets the URI and starts playback.}
|
[#:metadata metadata string? ""]
|
||||||
@defproc[(media-renderer-pause! [renderer media-renderer?]) void?]{Pauses playback.}
|
[#:next-uri next-uri (or/c #f string?) #f]
|
||||||
@defproc[(media-renderer-stop! [renderer media-renderer?]) void?]{Stops playback.}
|
[#:next-metadata next-metadata string? ""])
|
||||||
@defproc[(media-renderer-seek! [renderer media-renderer?] [seconds nonnegative-real?]) void?]{Seeks to a relative time.}
|
any]{
|
||||||
@defproc[(media-renderer-status [renderer media-renderer?]) symbol?]{Returns the abstracted transport state.}
|
|
||||||
@defproc[(media-renderer-position [renderer media-renderer?]) transport-position?]{Returns current position information.}
|
|
||||||
@defproc[(media-renderer-volume [renderer media-renderer?]) (or/c #f exact-integer?)]{Returns the device-specific volume value.}
|
|
||||||
@defproc[(media-renderer-set-volume! [renderer media-renderer?] [volume (integer-in 0 65535)]) void?]{Sets the device-specific volume value.}
|
|
||||||
@defproc[(media-renderer-muted? [renderer media-renderer?]) boolean?]{Returns the mute state.}
|
|
||||||
@defproc[(media-renderer-set-muted! [renderer media-renderer?] [muted? boolean?]) void?]{Sets the mute state.}
|
|
||||||
|
|
||||||
@section[#:tag "media-renderer-example"]{Example}
|
Sets @racket[uri] as the current resource and starts playback.
|
||||||
|
|
||||||
@racketblock[
|
When @racket[next-uri] is supplied, it is installed with
|
||||||
(define renderer
|
@tt{SetNextAVTransportURI} before playback starts. A supporting renderer can
|
||||||
(car (query-media-renderers #:interface "10.7.3.118")))
|
open and buffer the next resource while the current resource is playing,
|
||||||
|
allowing a gapless or near-gapless transition.
|
||||||
|
}
|
||||||
|
|
||||||
(media-renderer-play-uri!
|
@defproc[(media-renderer-next-uri-supported?
|
||||||
renderer
|
[renderer media-renderer?])
|
||||||
"http://10.7.3.32:50002/music/track.flac")
|
boolean?]{
|
||||||
|
|
||||||
(media-renderer-set-volume! renderer 30)
|
Reports whether the renderer advertises the optional
|
||||||
]
|
@tt{SetNextAVTransportURI} action.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-renderer-set-next-uri!
|
||||||
|
[renderer media-renderer?]
|
||||||
|
[uri string?]
|
||||||
|
[#:metadata metadata string? ""])
|
||||||
|
any]{
|
||||||
|
|
||||||
|
Sets the resource that follows the currently playing resource. The action
|
||||||
|
describes one next resource, not a complete queue. After playback advances to
|
||||||
|
that resource, call this function again to preload the following track.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-renderer-pause! [renderer media-renderer?]) any]
|
||||||
|
@defproc[(media-renderer-stop! [renderer media-renderer?]) any]
|
||||||
|
@defproc[(media-renderer-seek!
|
||||||
|
[renderer media-renderer?]
|
||||||
|
[seconds exact-nonnegative-integer?])
|
||||||
|
any]
|
||||||
|
@defproc[(media-renderer-status [renderer media-renderer?]) symbol?]
|
||||||
|
@defproc[(media-renderer-position [renderer media-renderer?])
|
||||||
|
transport-position?]
|
||||||
|
|
||||||
|
@defproc[(transport-position-track
|
||||||
|
[position transport-position?])
|
||||||
|
exact-nonnegative-integer?]
|
||||||
|
@defproc[(transport-position-seconds
|
||||||
|
[position transport-position?])
|
||||||
|
(or/c #f number?)]
|
||||||
|
@defproc[(transport-position-duration
|
||||||
|
[position transport-position?])
|
||||||
|
(or/c #f number?)]
|
||||||
|
@defproc[(transport-position-uri
|
||||||
|
[position transport-position?])
|
||||||
|
(or/c #f string?)]
|
||||||
|
|
||||||
|
@section{Volume and mute}
|
||||||
|
|
||||||
|
@defproc[(media-renderer-volume [renderer media-renderer?])
|
||||||
|
exact-nonnegative-integer?]
|
||||||
|
@defproc[(media-renderer-set-volume!
|
||||||
|
[renderer media-renderer?]
|
||||||
|
[volume exact-nonnegative-integer?])
|
||||||
|
any]
|
||||||
|
@defproc[(media-renderer-muted? [renderer media-renderer?]) boolean?]
|
||||||
|
@defproc[(media-renderer-set-muted!
|
||||||
|
[renderer media-renderer?]
|
||||||
|
[muted? boolean?])
|
||||||
|
any]
|
||||||
|
|||||||
@@ -1,114 +1,120 @@
|
|||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@(require (for-label racket/base
|
@(require
|
||||||
racket/contract
|
(for-label
|
||||||
racket/list
|
racket/base
|
||||||
(file "../main.rkt")
|
|
||||||
(file "../media-server.rkt")))
|
(file "../media-server.rkt")))
|
||||||
|
|
||||||
@title{Media Server Browser}
|
@title{Media Servers}
|
||||||
|
|
||||||
@defmodule[@racketmodname[racket-upnp/media-server] #:module-paths ((file "../media-server.rkt"))]
|
@defmodule[(file "../media-server.rkt")]
|
||||||
|
|
||||||
This module turns the DIDL-Lite returned by
|
This module provides a higher-level browser for UPnP and DLNA media servers.
|
||||||
@racketmodname[racket-upnp/services/content-directory] into ordinary Racket
|
It converts ContentDirectory DIDL-Lite XML into containers, items, and media
|
||||||
values. Containers are browsed one level at a time; the module does not
|
resources.
|
||||||
recursively load an entire media library. This is suitable for a browser that
|
|
||||||
requests children when the user expands a container.
|
|
||||||
|
|
||||||
The hierarchy is the logical hierarchy published by the media server. A server
|
@defproc[(query-media-servers
|
||||||
may offer views such as artist, album, genre, and physical folder, so a track
|
[#:interface interface (or/c #f string?) #f]
|
||||||
can occur in more than one container.
|
[#:dns? dns? boolean? #f])
|
||||||
|
(listof media-server?)]{
|
||||||
|
|
||||||
|
Discovers media servers.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(get-media-server [filter-name string?])
|
||||||
|
(or/c #f media-server?)]{
|
||||||
|
|
||||||
|
Returns the first discovered media server whose name contains
|
||||||
|
@racket[filter-name], ignoring case, or @racket[#f].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(media-server? [value any/c]) boolean?]
|
||||||
|
@defproc[(media-server-name [server media-server?]) string?]
|
||||||
|
@defproc[(media-server-address [server media-server?]) string?]
|
||||||
|
@defproc[(media-server-manufacturer [server media-server?])
|
||||||
|
(or/c #f string?)]
|
||||||
|
@defproc[(media-server-model [server media-server?])
|
||||||
|
(or/c #f string?)]
|
||||||
|
|
||||||
@section{Browsing}
|
@section{Browsing}
|
||||||
|
|
||||||
@defproc[(query-media-servers [#:interface interface (or/c #f string?) #f]
|
@defproc[(media-server-root
|
||||||
[#:dns? dns? boolean? #f])
|
[server media-server?]
|
||||||
(listof media-server?)]{
|
|
||||||
Discovers UPnP MediaServer devices that provide a ContentDirectory service.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(media-server? [value any/c]) boolean?]{
|
|
||||||
Recognises a MediaServer device with a ContentDirectory service.
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(media-server-root [server media-server?]
|
|
||||||
[#:start start exact-nonnegative-integer? 0]
|
[#:start start exact-nonnegative-integer? 0]
|
||||||
[#:count count exact-nonnegative-integer? 0])
|
[#:count count exact-nonnegative-integer? 0])
|
||||||
(listof media-entry?)]{
|
(listof media-entry?)]{
|
||||||
Returns the direct children of ContentDirectory object @tt{0}. The optional
|
|
||||||
paging arguments are passed to ContentDirectory. A count of zero asks the
|
Browses object @tt{0}, the ContentDirectory root.
|
||||||
server to return all available children, subject to the server's own limits.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(media-server-browse [server media-server?]
|
@defproc[(media-server-browse
|
||||||
|
[server media-server?]
|
||||||
[container (or/c string? media-container?) "0"]
|
[container (or/c string? media-container?) "0"]
|
||||||
[#:start start exact-nonnegative-integer? 0]
|
[#:start start exact-nonnegative-integer? 0]
|
||||||
[#:count count exact-nonnegative-integer? 0])
|
[#:count count exact-nonnegative-integer? 0])
|
||||||
(listof media-entry?)]{
|
(listof media-entry?)]{
|
||||||
Returns the direct children of an object identifier or previously returned
|
|
||||||
container.
|
Browses one level below a container ID or a previously returned container.
|
||||||
|
|
||||||
|
In ContentDirectory, @racket[count] equal to zero asks the server to return all
|
||||||
|
remaining entries. Use a positive count for large containers.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(media-container-children [server media-server?]
|
@defproc[(media-container-children
|
||||||
|
[server media-server?]
|
||||||
[container media-container?]
|
[container media-container?]
|
||||||
[#:start start exact-nonnegative-integer? 0]
|
[#:start start exact-nonnegative-integer? 0]
|
||||||
[#:count count exact-nonnegative-integer? 0])
|
[#:count count exact-nonnegative-integer? 0])
|
||||||
(listof media-entry?)]{
|
(listof media-entry?)]{
|
||||||
Convenience form of @racket[media-server-browse] for a container value.
|
|
||||||
|
Equivalent to browsing the supplied container.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section{Entries}
|
@section{Entries}
|
||||||
|
|
||||||
@defproc[(media-entry? [value any/c]) boolean?]{Recognises a media container or media item.}
|
@defproc[(media-entry? [value any/c]) boolean?]
|
||||||
@defproc[(media-entry-id [entry media-entry?]) (or/c #f string?)]{Returns the ContentDirectory object identifier.}
|
@defproc[(media-entry-id [entry media-entry?]) (or/c #f string?)]
|
||||||
@defproc[(media-entry-parent-id [entry media-entry?]) (or/c #f string?)]{Returns the parent object identifier.}
|
@defproc[(media-entry-parent-id [entry media-entry?]) (or/c #f string?)]
|
||||||
@defproc[(media-entry-title [entry media-entry?]) string?]{Returns the displayed title.}
|
@defproc[(media-entry-title [entry media-entry?]) string?]
|
||||||
@defproc[(media-entry-class [entry media-entry?]) (or/c #f string?)]{Returns the original UPnP class, such as @tt{object.item.audioItem.musicTrack}.}
|
@defproc[(media-entry-class [entry media-entry?]) (or/c #f string?)]
|
||||||
@defproc[(media-entry-restricted? [entry media-entry?]) boolean?]{Reports whether the server marks the object as restricted.}
|
@defproc[(media-entry-restricted? [entry media-entry?]) boolean?]
|
||||||
|
|
||||||
@defproc[(media-container? [value any/c]) boolean?]{Recognises a browsable container.}
|
@defproc[(media-container? [value any/c]) boolean?]
|
||||||
@defproc[(media-container-child-count [container media-container?]) (or/c #f exact-nonnegative-integer?)]{Returns the advertised number of children, when present.}
|
@defproc[(media-container-child-count [container media-container?])
|
||||||
@defproc[(media-container-searchable? [container media-container?]) boolean?]{Reports whether the container is advertised as searchable.}
|
(or/c #f exact-nonnegative-integer?)]
|
||||||
|
@defproc[(media-container-searchable? [container media-container?])
|
||||||
|
boolean?]
|
||||||
|
|
||||||
@defproc[(media-item? [value any/c]) boolean?]{Recognises a media item.}
|
@defproc[(media-item? [value any/c]) boolean?]
|
||||||
@defproc[(media-item-creator [item media-item?]) (or/c #f string?)]{Returns the Dublin Core creator.}
|
@defproc[(media-item-creator [item media-item?]) (or/c #f string?)]
|
||||||
@defproc[(media-item-artists [item media-item?]) (listof string?)]{Returns the advertised artists.}
|
@defproc[(media-item-artists [item media-item?]) (listof string?)]
|
||||||
@defproc[(media-item-album [item media-item?]) (or/c #f string?)]{Returns the album title.}
|
@defproc[(media-item-album [item media-item?]) (or/c #f string?)]
|
||||||
@defproc[(media-item-genres [item media-item?]) (listof string?)]{Returns the advertised genres.}
|
@defproc[(media-item-genres [item media-item?]) (listof string?)]
|
||||||
@defproc[(media-item-date [item media-item?]) (or/c #f string?)]{Returns the advertised date without imposing a date format.}
|
@defproc[(media-item-date [item media-item?]) (or/c #f string?)]
|
||||||
@defproc[(media-item-album-art-uri [item media-item?]) (or/c #f string?)]{Returns the album-art URI. Relative URIs are resolved against the device-description URL.}
|
@defproc[(media-item-album-art-uri [item media-item?])
|
||||||
@defproc[(media-item-resources [item media-item?]) (listof media-resource?)]{Returns all playable or retrievable resources. One item may advertise several encodings.}
|
(or/c #f string?)]
|
||||||
|
@defproc[(media-item-resources [item media-item?])
|
||||||
|
(listof media-resource?)]
|
||||||
|
|
||||||
@section{Resources}
|
@section{Resources}
|
||||||
|
|
||||||
@defproc[(media-resource? [value any/c]) boolean?]{Recognises a DIDL-Lite resource.}
|
@defproc[(media-resource? [value any/c]) boolean?]
|
||||||
@defproc[(media-resource-uri [resource media-resource?]) string?]{Returns the resource URI, resolved to an absolute URI where possible.}
|
@defproc[(media-resource-uri [resource media-resource?]) string?]
|
||||||
@defproc[(media-resource-protocol-info [resource media-resource?]) (or/c #f string?)]{Returns the complete UPnP @tt{protocolInfo} value.}
|
@defproc[(media-resource-protocol-info [resource media-resource?])
|
||||||
@defproc[(media-resource-content-type [resource media-resource?]) (or/c #f string?)]{Returns the content-format portion of @tt{protocolInfo}, such as @tt{audio/flac}.}
|
(or/c #f string?)]
|
||||||
@defproc[(media-resource-size [resource media-resource?]) (or/c #f exact-nonnegative-integer?)]{Returns the size in bytes.}
|
@defproc[(media-resource-content-type [resource media-resource?])
|
||||||
@defproc[(media-resource-duration [resource media-resource?]) (or/c #f nonnegative-real?)]{Returns the duration in seconds.}
|
(or/c #f string?)]
|
||||||
@defproc[(media-resource-bitrate [resource media-resource?]) (or/c #f exact-nonnegative-integer?)]{Returns the advertised bitrate in bytes per second, as defined by DIDL-Lite.}
|
@defproc[(media-resource-size [resource media-resource?])
|
||||||
@defproc[(media-resource-sample-frequency [resource media-resource?]) (or/c #f exact-nonnegative-integer?)]{Returns the sample frequency in hertz.}
|
(or/c #f exact-nonnegative-integer?)]
|
||||||
@defproc[(media-resource-bits-per-sample [resource media-resource?]) (or/c #f exact-nonnegative-integer?)]{Returns the advertised bits per sample.}
|
@defproc[(media-resource-duration [resource media-resource?])
|
||||||
@defproc[(media-resource-channels [resource media-resource?]) (or/c #f exact-nonnegative-integer?)]{Returns the number of audio channels.}
|
(or/c #f number?)]
|
||||||
@defproc[(media-resource-resolution [resource media-resource?]) (or/c #f string?)]{Returns an advertised image or video resolution.}
|
@defproc[(media-resource-bitrate [resource media-resource?])
|
||||||
|
(or/c #f exact-nonnegative-integer?)]
|
||||||
@section[#:tag "media-server-example"]{Example}
|
@defproc[(media-resource-sample-frequency [resource media-resource?])
|
||||||
|
(or/c #f exact-nonnegative-integer?)]
|
||||||
@racketblock[
|
@defproc[(media-resource-bits-per-sample [resource media-resource?])
|
||||||
(define synology
|
(or/c #f exact-nonnegative-integer?)]
|
||||||
(car (query-media-servers #:interface "10.7.3.118")))
|
@defproc[(media-resource-channels [resource media-resource?])
|
||||||
|
(or/c #f exact-nonnegative-integer?)]
|
||||||
(define root (media-server-root synology))
|
@defproc[(media-resource-resolution [resource media-resource?])
|
||||||
(define music
|
(or/c #f string?)]
|
||||||
(findf (lambda (entry)
|
|
||||||
(and (media-container? entry)
|
|
||||||
(string=? (media-entry-title entry) "Muziek")))
|
|
||||||
root))
|
|
||||||
|
|
||||||
(for ([entry (in-list (media-container-children synology music))])
|
|
||||||
(printf "~a: ~a\n"
|
|
||||||
(if (media-container? entry) 'container 'item)
|
|
||||||
(media-entry-title entry)))
|
|
||||||
]
|
|
||||||
|
|||||||
Reference in New Issue
Block a user