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.
Featured track algorithms
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.
Original | Extracted |
---|---|
03. Eli Brown - Escape (Original Mix) - 7A - 127.WAV | Eli Brown - Escape (Original Mix) |
Eli Brown - Escape (Original Mix) - 7A - 127.WAV | Eli Brown - Escape (Original Mix) |
- Escape (Original Mix) - 7A - 127.WAV | - Escape (Original Mix) |
The Space Between Feat. Laura Aqui (Assaf Remix) - 1A - 132 | The 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) - 1A | 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) | 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.
Variable | Description | Example output |
---|---|---|
$ARTIST$ | Track artist | Depeche Mode |
$SONG$ | Track title | Just Can’t Get Enough |
$GENRE$ | Track genre | New Wave |
$TIMESTAMP_YEAR$ | Year component of current time | 2022 |
$TIMESTAMP_MONTH$ | Zero-padded month component of current time | 03 |
$TIMESTAMP_DAY$ | Zero-padded day component of current time | 01 |
$TIMESTAMP_HOUR$ | Zero-padded hour component of current time | 09 |
$TIMESTAMP_MINUTE$ | Zero-padded minute component of current time | 22 |
$TIMESTAMP_SECOND$ | Zero-padded second component of current time | 49 |
$ELAPSED$ | Elapsed time since first track in current history | 00:01:24 |
The following table provides example format strings along with example output.
Format string | Example 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:
Format | Extension(s) |
---|---|
JSON | json |
TOML | toml |
YAML | yaml, yml |
HCL | hcl, tfvars |
Java config properties | properties, props, prop |
envfile | dotenv, env |
INI | ini |
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.