#LyX 1.4.3 created this file. For more info see http://www.lyx.org/ \lyxformat 245 \begin_document \begin_header \textclass docbook \language english \inputencoding auto \fontscheme default \graphics default \paperfontsize default \papersize default \use_geometry false \use_amsmath 0 \cite_engine basic \use_bibtopic false \paperorientation portrait \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \papercolumns 1 \papersides 1 \paperpagestyle default \tracking_changes false \output_changes true \end_header \begin_body \begin_layout Title MediaTomb Scripting \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard MediaTomb allows you to customize the structure of how your media is being presented to your renderer. One of the most important features introduced since the version 0.8 are the virtual containers and virtual items. Let's think of possible scenarios: \end_layout \begin_layout Itemize you may want to separate your content by music, photo, video, maybe create a special container with all non playable stuff \end_layout \begin_layout Itemize you may want your music to be sorted by genre, year, artist, album, or maybe by starting letters, so you can more easily find your favorite song when browsing the server \end_layout \begin_layout Itemize you want to have your photos that you took with your favorite digital camera to appear in a special folder, or maybe you even want to separate the photos that you took with flash-on from the ones that you made without flash \end_layout \begin_layout Itemize your media player does not support video, so you do not even want to see the Video container \end_layout \begin_layout Itemize it's up to your imagination :) \end_layout \begin_layout Standard The scenarios described above and much more can be achieved with the help of an import script. \end_layout \begin_layout Section How It Works \end_layout \begin_layout Standard This section will give you some overview on how virtual objects work and on how they are related to scripting. \end_layout \begin_layout Subsection Understanding Virtual Objects. \end_layout \begin_layout Standard When you add a file or directory to the database via the web interface several things happen. \end_layout \begin_layout Enumerate The object is inserted into the PC Directory. PC Directory is simply a special non-removable container. Any media file added will have an entry inside the PC Directory tree. PC Directory's hierarchy reflects the filesystem hierarchy, all objects inside the PC Directory including itself are NON-VIRTUAL objects. All virtual objects may have a different title, descripting, etc., but they are still references to objects in the PC-Directory. That's why it is not possible to change a location of a virtual object - the only exceptions are URL items and Active items. \end_layout \begin_layout Enumerate Once an item is added to the PC Directory it is forwarded to the virtual object engine. The virtual object engine's mission is to organize and present the media database in a logical hierarchy based on the available metadata of the items. \end_layout \begin_layout Standard Each UPnP server implements this so called virtual object hierarchy in a different way. Audio files are usually sorted by artist, album, some servers may just present a view similar to the filesystem and so on. Most servers have strong limitations on the structure of the virtual containers , they usually offer a predefined layout of data and the user has to live with it. In MediaTomb we try to address this shortcoming by introducing the scriptable virtual object engine. It is designed to be: \end_layout \begin_layout Itemize maximally flexible \end_layout \begin_layout Itemize easily customizable and extendable \end_layout \begin_layout Itemize robust and efficient \end_layout \begin_layout Standard We try to achieve these goals by embedding a scripting runtime environment that allows the execution of ECMAScript-262 conform scripts better known as JavaScript. We are using Mozilla's JavaScript implementation called SpiderMonkey, it is a stand-alone easily embeddable javascript engine, supporting JavaScript versions 1.0 through 1.4. \end_layout \begin_layout Subsection Theory Of Operation \end_layout \begin_layout Standard After an item is added to the PC Directory it is automatically fed as input to the import script. The script then creates one or more virtual items for the given original item. Items created from scripts are always marked virtual. \end_layout \begin_layout Standard When the virtual object engine gets notified of an added item, following happens: a javascript object is created mirroring the properties of the item. The object is introduced to the script environment and bound to the predefined variable 'orig'. This way a variable orig is always defined for every script invocation and represents the original data of the added item. Then the script is invoked. \end_layout \begin_layout Standard In the current implementation, if you modify the script then you will have to restart the server for the new logic to take effect. Note, that the script is only triggered when new objects are added to the database, also note that the script does not modify any objects that already exist in the database - it only processes new objects that are being added. \end_layout \begin_layout Section The Import Script \end_layout \begin_layout Standard In this section we will introduce the properties of the object that will be processed by the script, as well as functions that are offered by the server. \end_layout \begin_layout Subsection Global Variables And Constants \end_layout \begin_layout Subsubsection The 'orig' Object \end_layout \begin_layout Standard As described in Section 2.2, each time an item is added to the database the import script is invoked. So, one script invocation processes exactly one non virtual item, and creates a number of virtual items and containers. The original item is made available in the form of the global variable 'orig'. It is usually a good idea to only read from this variable and to create and only modify local copies. \end_layout \begin_layout Standard Here is a list of properties of an object, you can set them you create a new object or when you modify a copy of the 'orig' object. \end_layout \begin_layout Standard \emph on RW \emph default means read/write, i.e. - changes made to that property will be transferred into the database. \end_layout \begin_layout Standard \emph on RO \emph default means, that this is a readonly property, any changes made to it will get lost. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.objectType \end_layout \begin_layout Standard \emph on RW \end_layout \begin_layout Standard This defines the object type, following types are available: \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_CONTAINER \end_layout \begin_layout Standard Object is a container. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ITEM \end_layout \begin_layout Standard Object is an item. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ACTIVE_ITEM \end_layout \begin_layout Standard Object is an active item. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ITEM_EXTERNAL_URL \end_layout \begin_layout Standard Object is a link to a resouce on the internet. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ITEM_INTERNAL_URL \end_layout \begin_layout Standard Object is an internal link. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.title \end_layout \begin_layout Standard \family typewriter \emph on RW \end_layout \begin_layout Standard This is the title of the original object, since the object represents an entry in the PC-Directory, the title will be set to it's filename. This field corresponds to dc:title in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.id \end_layout \begin_layout Standard \family typewriter \emph on RO \end_layout \begin_layout Standard The object ID, make sure to set all refID's (reference IDs) of your virtual objects to that ID. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.parentID \end_layout \begin_layout Standard \family typewriter \emph on RO \end_layout \begin_layout Standard The object ID of the parent container. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.class \end_layout \begin_layout Standard \family typewriter \emph on RW \end_layout \begin_layout Standard The upnp class of the item, this corresponds to upnp:class in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.location \end_layout \begin_layout Standard \family typewriter \emph on RO \end_layout \begin_layout Standard Location on disk, given by the absolute path and filename. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.mimetype \end_layout \begin_layout Standard \family typewriter \emph on RW \end_layout \begin_layout Standard Mimetype of the object. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta \end_layout \begin_layout Standard \family typewriter \emph on RW \end_layout \begin_layout Standard Array holding the metadata that was extracted from the object (i.e. id3/exif/etc. information) \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta[M_TITLE] \end_layout \begin_layout Standard \family typewriter \emph on RW \end_layout \begin_layout Standard Extracted title (for example the id3 title if the object is an mp3 file), if you want that your new virtual object is displayed under this title you will have to set obj.title = orig.meta[M_TITLE] \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta[M_ARTIST] \end_layout \begin_layout Standard \emph on RW \end_layout \begin_layout Standard Artist information, this correponds to upnp:artist in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta[M_ALBUM] \end_layout \begin_layout Standard \family typewriter \emph on RW \end_layout \begin_layout Standard Album information, this corresponds to upnp:album in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta[M_DATE] \end_layout \begin_layout Standard \emph on RW \end_layout \begin_layout Standard Date, must be in the format of YYYY-MM-DD (required by the UPnP spec), this corresponds to dc:date in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta[M_GENRE] \end_layout \begin_layout Standard \family typewriter \emph on RW \end_layout \begin_layout Standard Genre of the item, this corresponds to upnp:genre in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta[M_DESCRIPTION] \end_layout \begin_layout Standard \emph on RW \end_layout \begin_layout Standard Description of the item, this corresponds to dc:description in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.meta[M_TRACKNUMBER] \end_layout \begin_layout Standard \emph on RW \end_layout \begin_layout Standard Track number of the item, this corresponds to upnp:originalTrackNumber in the DIDL-Lite XML. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code orig.aux \end_layout \begin_layout Standard \emph on RO \end_layout \begin_layout Standard Array holding the so called auxilary data. Aux data is metadata that is not part of UPnP, for example - this can be a camera model that was used to make a photo, or the information if the photo was taken with or without flash. Currently aux data can be gathered from libexif and libextractor (see the Import section in the main documentation for more details). So, this array will hold the tags that you specified in your config.xml, allowing you to create your virtual structure according to your liking. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Subsubsection Constants \end_layout \begin_layout Standard Actually there are no such things as constants in JS, so those are actually predefined global variables that are set during JS engine initialization. Do not assign any values to them, otherwise following script invocation will be using wrong values. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code UPNP_CLASS_CONTAINER \end_layout \begin_layout Standard \emph on Type: string \end_layout \begin_layout Standard \emph on Value: object.container \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code UPNP_CLASS_CONTAINER_MUSIC \end_layout \begin_layout Standard \emph on Type: string \end_layout \begin_layout Standard \emph on Value: object.container.musicContainer \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code UPNP_CLASS_CONTAINER_MUSIC_ARTIST \end_layout \begin_layout Standard \emph on Type: string \end_layout \begin_layout Standard \emph on Value: object.container.person.musicArtist \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code UPNP_CLASS_CONTINAER_MUSIC_GENRE \end_layout \begin_layout Standard \emph on Type: string \end_layout \begin_layout Standard \emph on Value: object.container.genre.musicGenre \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code UPNP_CLASS_CONTAINER_MUSIC_ALBUM \end_layout \begin_layout Standard \emph on Type: string \end_layout \begin_layout Standard \emph on Value: object.container.album.musicAlbum \end_layout \begin_layout Description Note: this container class will be treaded by the server in a special way, all music items in this container will be sorted by ID3 track number. \end_layout \begin_layout Standard \begin_inset ERT status collapsed \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code UPNP_CLASS_ITEM \end_layout \begin_layout Standard \emph on Type: string \end_layout \begin_layout Standard \emph on Value: object.item \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code UPNP_CLASS_ITEM_MUSIC_TRACK \end_layout \begin_layout Standard \emph on Type: string \end_layout \begin_layout Standard \emph on Value: object.item.audioItem.musicTrack \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_CONTAINER \end_layout \begin_layout Standard \emph on Type: integer \end_layout \begin_layout Standard \emph on Value: 1 \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ITEM \end_layout \begin_layout Standard \emph on Type: integer \end_layout \begin_layout Standard \emph on Value: 2 \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ACTIVE_ITEM \end_layout \begin_layout Standard \emph on Type: integer \end_layout \begin_layout Standard \emph on Value: 4 \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ITEM_EXTERNAL_URL \end_layout \begin_layout Standard \emph on Type: integer \end_layout \begin_layout Standard \emph on Value: 8 \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code OBJECT_TYPE_ITEM_INTERNAL_URL \end_layout \begin_layout Standard \emph on Type: integer \end_layout \begin_layout Standard \emph on Value: 16 \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Subsection Available Functions \end_layout \begin_layout Standard This section will introduce the functions that you can use in order to add your objects to the database. Some functions are offered by the server, and some helper functions are defined in your import script. \end_layout \begin_layout Subsubsection Server Functions \end_layout \begin_layout Standard The server offers three functions which can be called from within the import script: \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code addCdsObject(object, containerChain, lastContainerClass); \end_layout \begin_layout Standard This function adds a virtual object to the server database, the path in the database is defined by the containerChain parameter. The third argument is optional, it allows to set the upnp:class of the last container in the chain. \end_layout \begin_layout Standard Parameters: \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code object \end_layout \begin_layout Standard A virtual object that is either a copy of or a reference to 'orig', see Section 3.2 for a list of properties. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code containerChain \end_layout \begin_layout Standard A string, defining where the object will be added in the database hierarchy. The containers in the chain are separated by a slash '/', for example, a value of '/Audio/All Music' will add the object to the Audio, All Music container in the server hierarchy. Make sure to properly escape the slash characters in container names. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code lastContainerClass \end_layout \begin_layout Standard A string, defining the upnp:class of the container that appears last in the chain. This parameter can be ommited, in this case the default value 'object.container' will be taken. Setting specific upnp container classes is useful to define the special meaning of a particular container; for example, the server will always sort songs by track number if upnp class of a container is set to 'object.contai ner.album.musicAlbum'. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code copyObject(originalObject); \end_layout \begin_layout Standard This function returns a copy of the virtual object. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Code print(...); \end_layout \begin_layout Standard This function is useful for debugging scripts, it simply prints to the standard output. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Standard \end_layout \end_inset \end_layout \begin_layout Section Walkthrough \end_layout \begin_layout Standard Now it is time to take a closer look at the default import script that is supplied with MediaTomb. Usually it is installed in the /usr/share/mediatomb/js/import.js directory, but you will also find it in scripts/js/import.js in the MediaTomb source tree. \end_layout \begin_layout Description Note: this is not a JavaScript tutorial, if you are new to JS you should probably make yourself familiar with the language. \end_layout \begin_layout Subsection Helper Functions \end_layout \begin_layout Standard At the beginning of the script we define a couple of helper functions that we will use later on. \end_layout \begin_layout Standard The first function escapes slash '/' characters in a string. This is necessary, because the container chain is defined by a slash separated string, where slash has a special meaning - it defines the container hierarchy. That means, that slashes that appear in the object's title need to be properly escaped. \end_layout \begin_layout Code function escapeSlash(name) \end_layout \begin_layout Code { \end_layout \begin_layout Code name = name.replace(/ \backslash \backslash /g, " \backslash \backslash \backslash \backslash "); \end_layout \begin_layout Code name = name.replace(/ \backslash //g, " \backslash \backslash /"); \end_layout \begin_layout Code return name; \end_layout \begin_layout Code } \end_layout \begin_layout Code \end_layout \begin_layout Standard The following function makes it easier to work with container chains; it takes an array of container names as argument, makes sure that the names are properly escaped and adds the slash separators as necessary. It returns a string that is formatted to be used as a parameter for the addCdsObject function. \end_layout \begin_layout Code function createContainerChain(arr) \end_layout \begin_layout Code { \end_layout \begin_layout Code var path = ''; for (var i = 0; i < arr.length; i++) \end_layout \begin_layout Code { \end_layout \begin_layout Code path = path + '/' + escapeSlash(arr[i]); \end_layout \begin_layout Code } \end_layout \begin_layout Code return path; \end_layout \begin_layout Code } \end_layout \begin_layout Standard I just realized, that this may not be what we want... TODO Jin!!! \end_layout \begin_layout Code function normalizeDate(date) \end_layout \begin_layout Code { \end_layout \begin_layout Code var matches = date.match(/([0-9]+)-01-01/); \end_layout \begin_layout Code if (matches) \end_layout \begin_layout Code return matches[1]; \end_layout \begin_layout Code else \end_layout \begin_layout Code return date; \end_layout \begin_layout Code } \end_layout \begin_layout Standard Next, let's look at the functions that organize our content in the database by creating the virtual structure. Each media type - audio, image and video is handled by a separate function. \end_layout \begin_layout Standard The biggest one is the function that handles audio - the reason is simple: mp3 files offer a lot of metadata like album, artist, genre, etc. information, this allows us to create a nice container layout. \end_layout \begin_layout Code function addAudio(obj) \end_layout \begin_layout Code { \end_layout \begin_layout Code var desc = ''; \end_layout \begin_layout Code var artist_full; \end_layout \begin_layout Code var album_full; \end_layout \begin_layout Standard First we will gather all the metadata that is provided by our object, of course it is possible that some fields are empty - we will have to check that to make sure that we handle this case correctly. \end_layout \begin_layout Code var title = obj.meta[M_TITLE]; \end_layout \begin_layout Standard Note the difference between obj.title and obj.meta[M_TITLE] - while object.title will originally be set to the filename, obj.meta[M_TITLE] will contain the parsed title - in this particular example the ID3 title of an MP3. \end_layout \begin_layout Code \end_layout \begin_layout Code if (!title) title = obj.title; \end_layout \begin_layout Code var artist = obj.meta[M_ARTIST]; \end_layout \begin_layout Code if (!artist) \end_layout \begin_layout Code { \end_layout \begin_layout Code artist = 'Unknown'; \end_layout \begin_layout Code artist_full = null; \end_layout \begin_layout Code } \end_layout \begin_layout Code else \end_layout \begin_layout Code { \end_layout \begin_layout Code artist_full = artist; \end_layout \begin_layout Code desc = artist; \end_layout \begin_layout Code } \end_layout \begin_layout Code \end_layout \begin_layout Code var album = obj.meta[M_ALBUM]; \end_layout \begin_layout Code if (!album) \end_layout \begin_layout Code { \end_layout \begin_layout Code album = 'Unknown'; \end_layout \begin_layout Code album_full = null; \end_layout \begin_layout Code } \end_layout \begin_layout Code else \end_layout \begin_layout Code { \end_layout \begin_layout Code desc = desc + ', ' + album; \end_layout \begin_layout Code album_full = album; \end_layout \begin_layout Code } \end_layout \begin_layout Code \end_layout \begin_layout Code if (desc) \end_layout \begin_layout Code desc = desc + ', '; \end_layout \begin_layout Code \end_layout \begin_layout Code desc = desc + title; \end_layout \begin_layout Code \end_layout \begin_layout Code var date = obj.meta[M_DATE]; \end_layout \begin_layout Code if (!date) \end_layout \begin_layout Code { \end_layout \begin_layout Code date = 'Unknown'; \end_layout \begin_layout Code } \end_layout \begin_layout Code else \end_layout \begin_layout Code { \end_layout \begin_layout Code date = normalizeDate(date); \end_layout \begin_layout Code desc = desc + ', ' + date; \end_layout \begin_layout Code } \end_layout \begin_layout Code \end_layout \begin_layout Code var genre = obj.meta[M_GENRE]; \end_layout \begin_layout Code if (!genre) \end_layout \begin_layout Code { \end_layout \begin_layout Code genre = 'Unknown'; \end_layout \begin_layout Code } \end_layout \begin_layout Code else \end_layout \begin_layout Code { \end_layout \begin_layout Code desc = desc + ', ' + genre; \end_layout \begin_layout Code } \end_layout \begin_layout Code \end_layout \begin_layout Code var description = obj.meta[M_DESCRIPTION]; \end_layout \begin_layout Code if (!description) \end_layout \begin_layout Code { \end_layout \begin_layout Standard Note how we are setting properties of an object - in this case we put together a description and we are setting for objects that did not already have one. \end_layout \begin_layout Code obj.meta[M_DESCRIPTION] = desc; \end_layout \begin_layout Code } \end_layout \begin_layout Standard We finally gathered all data that we need, so let's create a nice layout for our audio files. Note how we are constructing the chain, in the line below the array 'chain' will be converted to 'Audio/All audio' by the createContainerChain() function. \end_layout \begin_layout Code var chain = new Array('Audio', 'All audio'); \end_layout \begin_layout Code obj.title = title; \end_layout \begin_layout Standard The UPnP class argument to addCdsObject() is optional, if it is not supplied the default UPnP class will be used. However, it is suggested to correctly set UPnP classes of containers and object - this information may be used by some renderers to identify the type of the container and present the content in a different manner . \end_layout \begin_layout Code addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC); \end_layout \begin_layout Code \end_layout \begin_layout Code chain = new Array('Audio', 'Artists', artist, 'All songs'); \end_layout \begin_layout Code addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC); \end_layout \begin_layout Code \end_layout \begin_layout Code chain = new Array('Audio', 'All - full name'); \end_layout \begin_layout Code var temp = ''; \end_layout \begin_layout Code if (artist_full) \end_layout \begin_layout Code temp = artist_full; \end_layout \begin_layout Code \end_layout \begin_layout Code if (album_full) \end_layout \begin_layout Code temp = temp + ' - ' + album_full + ' - '; \end_layout \begin_layout Code \end_layout \begin_layout Code obj.title = temp + title; \end_layout \begin_layout Code \end_layout \begin_layout Code addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC); \end_layout \begin_layout Code chain = new Array('Audio', 'Artists', artist, 'All - full name'); addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC); \end_layout \begin_layout Code chain = new Array('Audio', 'Artists', artist, album); obj.title = track + title; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CO NTAINER_MUSIC_ALBUM); \end_layout \begin_layout Code chain = new Array('Audio', 'Albums', album); obj.title = track + title; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_ MUSIC_ALBUM); \end_layout \begin_layout Code chain = new Array('Audio', 'Genres', genre); addCdsObject(obj, createCon tainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_GENRE); \end_layout \begin_layout Code chain = new Array('Audio', 'Year', date); addCdsObject(obj, createContai nerChain(chain), UPNP_CLASS_CONTAINER_MUSIC); } \end_layout \end_body \end_document