Skip to content

Releases: sonn-audio/node-upnp

v0.4.2

Choose a tag to compare

@rudyberends rudyberends released this 06 Sep 08:03

Opens the toolkit up to UPnP services beyond DLNA, and fixes a DIDL parsing bug that reached the media renderer.

GENA subscriber — start() now takes extraServices, so a caller can subscribe to a service the toolkit has no typed parser for, and onRaw reports every NOTIFY with its body and unescaped LastChange. Typed parsing is unchanged, but now only runs for AVTransport and RenderingControl instead of treating anything that isn't AVTransport as RenderingControl.

buildSoapRequest — instanceId: null omits the InstanceID argument, for services that take none (ZoneGroupTopology, DeviceProperties, ContentDirectory). The default is unchanged.

DIDL fix — <upnp:album[^>]*> also matched <upnp:albumArtURI>. Where a DIDL item lists the art before the album, reading upnp:album started at the wrong element and returned the art URI plus everything in between. This reaches UpnpMediaRenderer.onSetUri, so a control point using that field order handed us a garbage album name.

Also includes v0.4.1, which was tagged but never released: seek in ABS_TIME as well, and stop offering TRACK_NR.

v0.4.0

Choose a tag to compare

@rudyberends rudyberends released this 11 Aug 22:37
feat(controlpoint): ask a renderer which formats it accepts (v0.4.0)

A UPnP renderer publishes what it can decode as protocolInfo entries in its
ConnectionManager's Sink list, and asking is the only way to know whether a stream will
play before sending it: a renderer handed something it cannot decode does not complain,
it plays silence. Without this, a caller can only guess or be configured.

- discovery now resolves the ConnectionManager control URL alongside AVTransport and
  RenderingControl; the service parsing was already generic, so this is one more lookup.
- DlnaControlPoint.getSinkContentTypes() invokes GetProtocolInfo and returns the MIME
  types, cached — the sink list is a property of the device, not of a session.
- parseSinkContentTypes() is exported and takes the *third colon-separated field* of
  each entry rather than searching the raw string, so `audio/flac` cannot be found
  inside some other token, and it keeps parameterised types (`audio/L16;rate=44100`)
  intact.

Returns null — not an empty array — when the device has no ConnectionManager, does not
answer, or answers unparseably. "It told us nothing" and "it accepts nothing" must not
read the same to a caller deciding what to send.

v0.3.1

Choose a tag to compare

@rudyberends rudyberends released this 02 Aug 13:41
fix(ssdp): don't hang startup when the :1900 bind fails

SsdpAdvertiser.start() awaited only the bind success callback, so when
binding UDP :1900 errored — most often EADDRINUSE, when another UPnP/DLNA
process on the host already holds the port — the promise never settled and
start() hung forever. A caller that awaits start() during its own boot then
stalls indefinitely instead of finishing.

Treat a failed bind as best-effort: log a warning, leave the advertiser
disabled, and resolve so the caller's startup continues without UPnP.

v0.3.0

Choose a tag to compare

@rudyberends rudyberends released this 26 Jul 14:40
feat(didl): carry performer and genre on containers

DidlContainer could only express a title, art and a child count, so an album
container had no way to say whose album it is. Controllers use dc:creator and
upnp:artist to label and group album tiles, and browse-by-genre keys on
upnp:genre — all three were unreachable for hosts using this module.

Add artist, creator (falling back to artist) and genre, emitted in the DIDL
document. Existing callers are unaffected: every field is optional and the
element order for a container without them is unchanged.

v0.2.0

Choose a tag to compare

@rudyberends rudyberends released this 25 Jul 21:54
feat: UPnP Search action (v0.2.0)

Add optional Search to the MediaServer: a ContentProvider.search() hook, the
Search SOAP action + SCPD declaration, real GetSearchCapabilities (advertised
only when the provider implements search — empty otherwise, the spec signal that
stops control points from searching), and a parseSearchCriteria helper that
extracts free-text terms + a coarse item/container class filter from the raw
UPnP SearchCriteria grammar. Browse/Search share one response builder.

Verified end-to-end over SOAP: SCPD declares Search, GetSearchCapabilities
returns the fields, and a Search request reaches provider.search with the
container id and unescaped criteria.

v0.1.0

Choose a tag to compare

@rudyberends rudyberends released this 25 Jul 17:33
feat: initial @sonn-audio/node-upnp module

UPnP AV toolkit with zero runtime deps. Three app-agnostic frameworks:
- UpnpMediaServer  (inject a ContentProvider)
- UpnpMediaRenderer (inject a RendererHandler)
- DlnaControlPoint  (push to an external renderer, with device-quirk handling)

plus a shared SsdpAdvertiser and the underlying SSDP/SOAP/DIDL-Lite/GENA/
description/SCPD/ID3 primitives. Dual CJS+ESM build, typed. Includes a runnable
demo consumer and a release workflow (npm publish --provenance on release).