CLI

Commands

djctl provides several commands used to control its general operation. The following section provide an overview of each command and any accompanying flags.

completion

djctl can generate a shell completion file for bash, fish, powershell, and zsh. For detailed information on how to configure shell completion for a specific shell, please use the help command.

For example, use the following command for bash instructions:

$ ./djctl help completion bash

Example output:

Generate the autocompletion script for the bash shell.

This script depends on the 'bash-completion' package.
If it is not installed already, you can install it via your OS's package manager.

To load completions in your current shell session:

        source <(djctl completion bash)

To load completions for every new session, execute once:

#### Linux:

        djctl completion bash > /etc/bash_completion.d/djctl

#### macOS:

        djctl completion bash > /usr/local/etc/bash_completion.d/djctl

You will need to start a new shell for this setup to take effect.

help

Built-in help for commands and flags. Provides help for any command in the application.

Example usage:

$ ./djctl help start

Example output:

Observe and output track play events

Usage:
  djctl start [flags]

Flags:
      --cue.filename string               featured track cue sheet file
      --cue.header.file string            FILE header field
      --cue.header.filetype string        FILE format
      --cue.header.performer string       PERFORMER header field
      --cue.header.title string           TITLE header field
      --delay uint                        delay in seconds before track is declared featured
      --detection.algorithm string        featured track detection algorithm (default "volume")
      --detection.volume.percent uint     volume detection algorithm percent (default 100)
      --discord.channel string            Discord channel ID
      --discord.format string             Discord message format string (default "$ARTIST$ - $SONG$")
      --discord.token string              Discord API token
      --extract.regex.artist string       extract artist using named "artist" regex capture group
      --extract.regex.title string        extract title using named "title" regex capture group
  -h, --help                              help for start
      --hide.color string                 hide track when deck set to this color
      --hide.regex.artist string          hide track when regex matches artist
      --hide.regex.filename string        hide track when regex matches filename
      --hide.regex.genre string           hide track when regex matches genre
      --hide.regex.title string           hide track when regex matches title
      --history.filename string           featured track history file
      --history.format string             history line format string (default "$ARTIST$ - $SONG$")
      --http.addr string                  http listen address (default ":9090")
      --osc.address.artist string         OSC address for artist
      --osc.address.song string           OSC address for song
      --osc.host string                   OSC host
      --osc.port uint                     OSC port (default 7000)
      --outfile.art string                featured track album art png file
      --outfile.featured string           featured track file
      --outfile.format string             file format string (default "$ARTIST$ - $SONG$")
      --placeholder.art.internal string   album art placeholder internal image name
      --spotify.barcolor string           Spotify code bar color (default "white")
      --spotify.bgcolor string            Spotify code background color (default "000000")
      --spotify.id string                 Spotify ID
      --spotify.secret string             Spotify secret
      --spotify.timeout uint              Spotify code creation timeout in seconds (default 4)
      --spotify.width uint                Spotify code width (default 256)
      --target.device string              target device name
      --target.software string            target software name
      --theme.dir string                  custom theme directory
      --theme.internal string             internal theme name (default "default")
      --webhook.format string             custom field format string (default "$ARTIST$ - $SONG$")
      --webhook.json.art strings          album art key path in JSON webhook payload (default [art])
      --webhook.json.artist strings       artist key path in JSON webhoook payload (default [artist])
      --webhook.json.duration strings     duration key path in JSON webhoook payload (default [duration])
      --webhook.json.epoch strings        epoch timestamp key path in JSON webhoook payload (default [timestamp_epoch])
      --webhook.json.format strings       custom format field key path in JSON webhoook payload
      --webhook.json.genre strings        genre key path in JSON webhook payload (default [genre])
      --webhook.json.spotify strings      Spotify code key path in JSON webhook payload (default [spotify_code])
      --webhook.json.timestamp strings    timestamp key path in JSON webhoook payload (default [timestamp])
      --webhook.json.title strings        title key path in JSON webhook payload (default [title])
      --webhook.url string                featured track webhook url

Global Flags:
      --conf string          path to the configuration file
      --license.key string   license key
      --log.file string      log to filename
      --log.format string    one of text or json (default "text")
      --log.level string     one of debug, info, warn, error or fatal (default "info")
      --log.line             enable filename and line in logs

license

Decodes and displays information about a djctl license.

Example usage:

$ ./djctl license --license.key="FT7YCAYB...<REDACTED>"

Example output:

Registered to: Trusted Tester <placeholder@placeholder.com>
Version: 1
Expires: 2066-05-30

probe

Performs StagelinQ discovery, searching for compatible Denon devices on the network. When discovered, connects to the StateMap service on the device and attempts to dump supported StateMap values.

Example usage:

$ ./djctl probe --log.level=debug --log.file=probe.txt

In the above example, because the --log.file flag was used, no output appears on the terminal. Instead, after several minutes, the command line prompt will return and the probe.txt file will contain diagnostic information useful to the developer.

start

Launches djctl in server mode. This is the primary mode of operation and provides the following “featured track” sinks:

  • Discord
  • File
  • Webhook
  • WebSocket

Most users will interact with the WebSocket sink indirectly by way of displaying a built-in “theme” in an OBS browser source window.

The following section details notable features and flags used to control the behavior of the start command.

The --detection.algorithm flag allows selecting the featured track detection algorithm. By default, this algorithm defaults to volume. The other available algorithm is lead.

The volume algorithm promotes a track to “featured” state when it is the only audible track present in the mix. Other tracks that may have been playing during a transition must no longer be present in the mix. The volume threshold that defines whether a track is still in the mix is configurable using the --detection.volume.percent flag. When lowered from its default of 100, a track can become “featured” even when outgoing faders are still partially up. As long as all the other faders have dropped below a threshold (calculated as a percentage of the loudest track in the mix), a new track can be promoted to “featured.” To use this algorithm, set the --detection.algorithm=volume flag. Please note that this is the default algorithm.

The lead algorithm follows the active play state on a deck. A track is lead if you press play and no other track is also in the play state. When you engage play on a new track, this new track doesn’t transition into lead state until the other track has been stopped. To use this algorithm, set the --detection.algorithm=lead flag.

The altlead algorithm is similar to the lead algorithm. It is intended as an experimental approach to support SC players without an X series mixer. To use this algorithm, set the --detection.algorithm=altlead flag.

Track delay

The --delay flag provides a delayed “hold” or “queue” mechanism for tracks before publishing to a “featured track” sink.

Set this to a non-zero value in order to avoid momentary track cuts triggering a published track transition event. If you cut the fader to perform “drops” or cut mixing and scratching, you will likely want to adjust this value.

For example, setting delay to 3 would allow you to rapidly toggle the crossfader back and forth between channels without triggering a track transition. Instead, each new cut between channels preempts the last detected transition and resets the timer. Once no further transitions are detected within the 3 second window, the last detected transition is published to the “featured track” sink(s).

“Clean” metadata extraction

djctl can attempt to extract “clean” metadata through the use of regular expression capture groups. This is supported for both the title and artist metadata using the corresponding --extract.regex.title and --extract.regex.artist flags.

When extracting a title, the capture group must be named title and similarly when extracting an artist, it must be named artist. No capture group names besides title and artist are detected by djctl.

The title capture group name is expected when using --extract.regex.title and similarly the artist capture group name is expected when using --extract.regex.artist. djctl will not launch if you supply either of these flags without the required capture group name present in the regular expression.

The following example attempts to extract a “clean” track title by ignoring track number, Camelot key, BPM, and file extension:

--extract.regex.title='^(?P<num>\d+)?\.?\s?(?P<title>.+?)\s?-?\s?(?P<key>\d{1,2}[a-zA-Z])?\s?-?\s?(?P<bpm>\d{1,3})?(\.(?P<extension>\w+))?$'

Please note the surrounding single quotes; this is important and should prevent the shell from escaping special characters.

The following table illustrates an original title and an extracted title using the aforementioned example regular expression.

OriginalExtracted
03. Eli Brown - Escape (Original Mix) - 7A - 127.WAVEli Brown - Escape (Original Mix)
Eli Brown - Escape (Original Mix) - 7A - 127.WAVEli Brown - Escape (Original Mix)
- Escape (Original Mix) - 7A - 127.WAV- Escape (Original Mix)
The Space Between Feat. Laura Aqui (Assaf Remix) - 1A - 132The Space Between Feat. Laura Aqui (Assaf Remix)
The Space Between Feat. Laura Aqui (Assaf Remix) - 1A -The Space Between Feat. Laura Aqui (Assaf Remix)
The Space Between Feat. Laura Aqui (Assaf Remix) - 1AThe Space Between Feat. Laura Aqui (Assaf Remix)
The Space Between Feat. Laura Aqui (Assaf Remix) -The Space Between Feat. Laura Aqui (Assaf Remix)
The Space Between Feat. Laura Aqui (Assaf Remix)The Space Between Feat. Laura Aqui (Assaf Remix)

When developing and testing regular expression capture group extractions, please use the regex101.com web site in Golang mode. This will use the same extraction engine djctl uses internally.

Hiding (“DJ AM” mode)

djctl supports two modes of track hiding. The simplest and most versatile is using the --hide.color flag. The second and more advanced method is regular expression matching using the --hide.regex.artist, --hide.regex.title, --hide.regex.genre, and --hide.regex.filename flags.

Color

If you would like the ability to dynamically hide tracks while DJing, use the --hide.color flag. When the EngineOS deck color matches the specified --hide.color flag value, djctl will hide the track information. When it no longer matches, the track information is revealed. You can toggle this color change while DJing, forcing a track to dynamically hide and unhide.

The following color names are recognized:

  • red
  • purple
  • red
  • orange
  • yellow
  • limegreen
  • green
  • lightblue
  • blue
Regular expression

You can also specify a regular expression to trigger track hiding with the --hide.regex.artist, --hide.regex.title, --hide.regex.genre, and --hide.regex.filename flags.

For example, you can automatically hide tracks whose track title metadata contains “[ID]” at the beginning with the following flag:

--hide.regex.title='^\[ID\].*'

Please note that when any of the --hide.regex.* flags encounter a match, all track metadata (title, artist, and art) will become hidden.

HTTP listener

The djctl start command will automatically launch an HTTP listener on port 9090. If you need to change this for any reason, you can use the --http.addr flag.

By default, the listener will bind to all addresses. You can restrict this to a specific address such as localhost. For example:

$ ./djctl start --http.addr=127.0.0.1:9090

This may be preferred when operating on a “hostile” network that isn’t under your full control.

Please see the WebSocket API documentation for more information.

File outputs

djctl can sink featured track information to a file. The --outfile.featured string should contain the file name destination of the featured track sink. This file will be overwritten each time a track transition event occurs. If this string is empty, no file sink updates will occur. Use the --outfile.featured flag to enable this feature along with --outfile.format to control the contents of the featured track file. Please see the substitution variables section for more information about constructing format specifiers.

To output album art, specify an --outfile.art file location. The album art is always a 256x256 resolution PNG file. It is the same art that appears on the jogwheel of the Denon device. When no album art is unavailable, a 1-pixel transparent PNG file is written instead.

To output a historical log of tracks played, use the --history.filename and --history.format flags. During each track transition, a line will be appended to the file specified in --history.filename. Use the --history.filename flag to enable this feature along with --history.format to control the contents of the history file. Please see the substitution variables section for more information about constructing format specifiers.

Substitution variables

djctl supports a basic substitution mechanism where variable strings such as $ARTIST$ and $SONG$ are replaced with artist and title metadata respectively. The default behavior uses $ARTIST$ - $SONG$ as the format string for both --outfile.format and --history.format flags. With this format string, the song “Just Can’t Get Enough” by the artist “Depeche Mode” outputs Depeche Mode - Just Can't Get Enough.

The following table describes the substitution variables available for use with the --outfile.format, --history.format, --discord.format, and --webhook.format flags.

VariableDescriptionExample output
$ARTIST$Track artistDepeche Mode
$SONG$Track titleJust Can’t Get Enough
$GENRE$Track genreNew Wave
$TIMESTAMP_YEAR$Year component of current time2022
$TIMESTAMP_MONTH$Zero-padded month component of current time03
$TIMESTAMP_DAY$Zero-padded day component of current time01
$TIMESTAMP_HOUR$Zero-padded hour component of current time09
$TIMESTAMP_MINUTE$Zero-padded minute component of current time22
$TIMESTAMP_SECOND$Zero-padded second component of current time49
$ELAPSED$Elapsed time since first track in current history00:01:24

The following table provides example format strings along with example output.

Format stringExample output
$ARTIST$ - $SONG$Depeche Mode - Just Can’t Get Enough
$TIMESTAMP_HOUR$:$TIMESTAMP_MINUTE$:$TIMESTAMP_SECOND$ - $ARTIST$ - $SONG$20:07:43 - Depeche Mode - Just Can’t Get Enough

Discord

djctl can sink featured track information to a Discord channel. Use the --discord.token, --discord.channel, and --discord.format flags to configure. This documentation will be updated at a later date with detailed setup instructions.

Open Sound Control (OSC)

Documentation coming soon.

CUE sheet

To facilitate uploading of track listing data to Mixcloud, djctl can perform CUE sheet generation.

Use --cue.filename to output a CUE formatted track log to the specified file. To supply CUE header fields, use --cue.header.file, --cue.header.filetype, --cue.header.performer, and --cue.header.title.

For example, the following flags will generate a CUE sheet file.

--cue.filename=mix.cue \
--cue.header.file="Matt in the Mix.mp3" \
--cue.header.filetype="MP3" \
--cue.header.performer="DJ Matt" \
--cue.header.title="Matt in the Mix"

The following is an example CUE sheet generated with djctl.

PERFORMER "DJ Matt"
TITLE "Matt in the Mix"
FILE "Matt in the Mix.mp3" MP3
  TRACK 01 AUDIO
    TITLE "Where Are You Now - Kungs Remix"
    PERFORMER "Lost Frequencies"
    INDEX 01 00:00:00
  TRACK 02 AUDIO
    TITLE "I've Been Thinking About You - Klaas Remix"
    PERFORMER "Klaas"
    INDEX 01 00:13:00
  TRACK 03 AUDIO
    TITLE "Ice Life - Nu Disco Mix"
    PERFORMER "Lissat"
    INDEX 01 00:17:00
  TRACK 04 AUDIO
    TITLE "Acid Flex"
    PERFORMER "Andy Buchan"
    INDEX 01 00:22:00

Please note, the 00:00:00 time marker starts when the first featured track is observed.

Spotify

For users with access to a Spotify Premium account, djctl can augment track metadata with a Spotify Code. The integration currently leverages the client credentials authentication flow which requires a Client ID and Client Secret to generate authentication tokens. To create a Spotify Client ID and Client Secret, follow these steps:

  • Visit https://developer.spotify.com/dashboard/applications.
  • Authenticate with your Spotify Premium account.
  • Click “Create an app”.
  • Enter “djctl” in the “App name” field.
  • Enter “dj control” in the “App description” field.
  • Select the checkboxes and submit.

Once created, the “Client ID” is displayed. Click on “Show client secret” to reveal the “Client secret” field. These values will need to be passed as parameters to djctl using the --spotify.id and --spotify.secret parameters. Both are required to successfully authenticate to the Spotify API.

The Spotify Code visual format can be customized using the --spotify.barcolor, --spotify.bgcolor, and --spotify.width parameters. The default Spotify Code is a white bar code on a black background. The image width is 256 pixels in order to match the width of the track art. You are free to override these values to match other use cases and aesthetics.

For the --spotify.barcolor option, either the value white or black are valid. The --spotify.bgcolor option should be specified as a three digit hex representation of an RGB color. Please omit the # character; for example, the color #0099cc should be passed as --spotify.bgcolor=0099cc. The width of the image can also be specified using the --spotify.width parameter. The height is not configurable and is a fixed ratio based upon the width.

Because the generation of a Spotify Code requires several API calls to remote systems, a default timeout of 4 seconds is enforced. This can be overriden with the --spotify.timeout parameter.

All Spotify Codes are cached for the duration of the djctl process lifetime. Practically speaking, this allows djctl to avoid excessive requests to the Spotify API. It also means that djctl memory consumption will slowly grow during long DJ sessions as the cache grows. Future versions of djctl may allow expiration of this cache during the process lifetime.

Currently, the SliderSpotify theme supports display of Spotify Codes. To enable this theme, use the --theme.dir=./themes/SliderSpotify/ option.

Targets

If you have multiple Denon devices present on your network, you may wish to use the --target.device and --target.software flags to target a specific device. In most cases, you will never find the need to use this flag.

For example, the Denon Prime 4 presents itself as the prime4 device running software JC11. See the example below for an example of how to target this device when launching djctl.

$ ./djctl start --target.device=prime4 --target.software=JC11

Themes

A small number of themes are embedded within the djctl binary. You can switch between them using the --theme.internal flag. Valid themes included default, text, slider, sliderspotify, artleft, artright, sliderartleft, and sliderartright. The HTML, CSS, and Javascript source for each of these themes is included in the examples/themes directory. To use a customized theme, use the --theme.dir flag to specify the root of the custom theme directory.

For example, the following command will instruct djctl to serve a custom theme located in the themes/CoolTheme/ directory directly below the current working directory.

$ ./djctl start --theme.dir=./themes/CoolTheme/

By default, djctl will use the default internal theme (--theme.internal=default flag). Please note that the presence of --theme.dir overrides the --theme.internal flag.

Placeholders

The --placeholder.art.internal flag allows selection of a “placeholder” track art image to use when art metadata is unavailable. Valid placeholder art names include pixel and note. The pixel image is a 1 pixel transparent PNG while the note image is a music note.

Webhook

djctl can be configured to send a webhook during track transition events. The following example will configure djctl to send webhooks to http://127.0.0.1:9091/webhook/ after each track transition event:

$ ./djctl start --webhook.url="http://127.0.0.1:9091/webhook/"

Please see the Webhook API documentation for more information.

version

Shows djctl build and version information.

Example output:

WARN[0000] No configuration file found
Build info: {0.5.0 3272ba63da0ea201a1ce10477752894d5360bed0 2022-02-18T04:14:43Z goreleaser}

Global flags

All commands provide a common set of global flags as described in the following sections.

Logging

djctl output is entirely composed of diagnostic log messages. You can control the verbosity of these log messages using the --log.level flag. The following log levels are available:

  • debug
  • info
  • warn
  • error
  • fatal

By default, djctl runs with a logging verbosity of info.

Log messages can be output in either plain text format or in a structured JSON format.

The default text log event is semi-structured into key/value pairs. For example:

time="2022-02-19T15:05:40-08:00" level=info msg="Requesting device service capabilities" deviceIP=192.168.68.198
time="2022-02-19T15:05:40-08:00" level=info msg="Service discovered" deviceIP=192.168.68.198 serviceName=StateMap servicePort=35349

With the --log.format=json flag, log messages can be emitted in JSON format. For example:

{"deviceIP":"192.168.68.198","level":"info","msg":"Requesting device service capabilities","time":"2022-02-19T15:06:41-08:00"}
{"deviceIP":"192.168.68.198","level":"info","msg":"Service discovered","serviceName":"StateMap","servicePort":35349,"time":"2022-02-19T15:06:41-08:00"}

For diagnostic purposes, you can also include the source code filename and line number information in the logs. This is controlled through the use of the --log.line flag which by default is not enabled. Notice the _source field the following example:

time="2022-02-19T15:09:33-08:00" level=info msg="Requesting device service capabilities" _source="start/start.go:387" deviceIP=192.168.68.198
time="2022-02-19T15:09:34-08:00" level=info msg="Service discovered" _source="start/start.go:401" deviceIP=192.168.68.198 serviceName=FileTransfer servicePort=38769

Finally, to output logs to a file, use the --log-file flag to specify a file path. When this flag is used, no output will appear on the terminal screen.

Configuration file

djctl supports the following configuration file formats:

FormatExtension(s)
JSONjson
TOMLtoml
YAMLyaml, yml
HCLhcl, tfvars
Java config propertiesproperties, props, prop
envfiledotenv, env
INIini

Please see the example YAML file in the djctl examples/ directory. When crafting a configuration file, the file format will follow the same hierarchy used in the CLI command flags. For example, --log.file=logs.txt would be expressed as the following YAML:

---
log:
  file: logs.txt

To instruct djctl to use a configuration file, use the --conf flag to specify its location. The default behavior of djctl is to search the current working directory for a file named conf with an extension of the aforementioned configuration file formats. For example, djctl will automatically search for a file named conf.yaml.

License

To use your personal license, please launch djctl with the --license.key command-line parameter followed by the license key text enclosed in double quotes. For example:

$ ./djctl start --license.key="ASBBDE...<redacted>"

Please see the configuration file section for information on how to create a configuration file where you can permanently place your license key.