Skip to main content
All XML responses served by the media server are rendered via Edge.js templates stored in the resources/ directory. This keeps XML structure separate from TypeScript business logic — templates can be edited and the server restarted without any TypeScript compilation step.
After editing any .edge template file, simply restart the server. No TypeScript compilation is needed — Edge.js reads and renders the template files at runtime.

How templates are mounted and rendered

import edge from 'edge.js';
import path from 'node:path';

edge.mount('default', path.resolve('resources'));
Calling edge.mount('default', ...) registers the resources/ directory as the default template root. Any .edge file in that directory is then available by name (without extension):
// First argument: template name (no extension)
// Second argument: data object passed to the template
const xml = await edge.render('Browse', gallery);

Templates

description.edge — Device description

Served at GET /description.xml. Tells UPnP control points everything they need to know about this device: its identity, friendly name, and the list of services it exposes. Variables:
VariableTypeDescription
uuidstringUnique device identifier (random UUID per process)
ipstringLocal IPv4 address of the server
portnumberHTTP port (always 8080)
friendlyNamestringHuman-readable name shown in control point UIs
manufacturerstringManufacturer name
manufacturerURLstringManufacturer website URL
The template declares three UPnP services in its <serviceList>:
Service typeControl URLEvent URL
ContentDirectory:1/upnp/control/ContentDirectory/upnp/event/ContentDirectory
ConnectionManager:1/upnp/control/ConnectionManager/upnp/event/ConnectionManager
AVTransport:1/upnp/control/AVTransport/upnp/event/AVTransport

Browse.edge — DIDL-Lite video list

Returned in response to a ContentDirectory Browse SOAP action. Renders a DIDL-Lite XML document listing all available video items. Variables:
VariableTypeDescription
videosobject[]Array of video items from gallery.json
video.idstringUnique item identifier
video.titlestringDisplay title
video.mimeTypestringMIME type (e.g. video/mp4)
video.urlstringPlayback URL for the media file
metadata.totalVideosnumberTotal number of videos in the catalog
metadata.versionnumberCatalog update ID
Full template:
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" ...>
  <s:Body>
    <u:BrowseResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
      <Result>
        @each(video in videos)
          <DIDL-Lite ...>
            <item id="{{ video.id }}" parentID="0" restricted="1">
              <dc:title>{{ video.title }}</dc:title>
              <upnp:class>object.item.videoItem</upnp:class>
              <res protocolInfo="http-get:*:{{ video.mimeType }}:*">{{ video.url }}</res>
            </item>
          </DIDL-Lite>
        @end
      </Result>
      <NumberReturned>{{ videos.length }}</NumberReturned>
      <TotalMatches>{{ metadata.totalVideos }}</TotalMatches>
      <UpdateID>{{ metadata.version }}</UpdateID>
    </u:BrowseResponse>
  </s:Body>
</s:Envelope>

GetSortCapabilities.edge — Sort capabilities

Returned in response to the GetSortCapabilities SOAP action. This is a static response with no template variables, returning an empty SortCapabilities element to indicate no sort criteria are imposed.
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:GetSortCapabilitiesResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
      <SortCapabilities></SortCapabilities>
    </u:GetSortCapabilitiesResponse>
  </s:Body>
</s:Envelope>

GetSearchCapabilities.edge — Search capabilities

Returned in response to the GetSearchCapabilities SOAP action. This is a static response with no template variables, returning * in SearchCaps to indicate all search properties are supported.
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:GetSearchCapabilitiesResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
      <SearchCaps>*</SearchCaps>
    </u:GetSearchCapabilitiesResponse>
  </s:Body>
</s:Envelope>

Template summary

Template fileSOAP / HTTP triggerHas variables?Purpose
description.edgeGET /description.xmlYesUPnP device description
Browse.edgeBrowse actionYesDIDL-Lite video listing
GetSortCapabilities.edgeGetSortCapabilities actionNoReturns empty SortCapabilities (no sort constraints)
GetSearchCapabilities.edgeGetSearchCapabilities actionNoReturns SearchCaps: * (all search properties supported)