Command line

spytifyd records; the Spytify app and spytify-cli talk to it over a local socket.

spytifyd &                     # logs the recorded application on its first line
spytify-cli info               # versions and the startup checks
spytify-cli set encode.format=flac output.dir=~/Music/Recordings
spytify-cli start              # recording starts with the next track
spytify-cli status
spytify-cli tail -f            # follow the log
spytify-cli stop               # the current track finishes first; --now stops at once
spytify-cli keep               # saves the last track cut short, as "<title> (partial)"
spytify-cli What it does
info versions, recorded application, startup checks
start [--single-track] / stop [--now] / status the recording session
set key=value... / get [key...] / reset [prefix] settings, validated by the recorder
tail [-n N] [-f] / events log lines / every event as JSON lines
control play|pause|next|previous|restart transport; restart plays the current track again from 0:00
launch opens the application if it is not running
keep [id] saves the last track cut short (skipped, stopped with --now, or moved along its track bar) as “title (partial)”, until the next track is saved
service install|uninstall|status runs spytifyd at login and again after a crash (Linux, macOS)
shutdown stops the recorder

spytifyd --once --set output.dir=/srv/rec records one session without a socket, until the application stops or stays idle. spytifyd --fake timeline.json plays a scripted timeline through a built-in fake player, to try everything without an application.

Configuration

The recorded application is set with environment variables, read when the recorder starts, from the environment or a .env file: next to the binary, in the working folder, or in your configuration folder (~/.config/spytify/.env, ~/Library/Application Support/spytify/.env on macOS, %APPDATA%\spytify\.env on Windows). The app’s Player settings write that last one.

Variable Used for Default
SPYTIFY_TARGET_PROCESS the application’s name, or a comma list for several packagings the build’s default
SPYTIFY_TARGET_MPRIS its D-Bus name (Linux) org.mpris.MediaPlayer2.<name>
SPYTIFY_TARGET_BUNDLE_ID its bundle id (macOS) read from the installed app
SPYTIFY_TARGET_DURATION_UNIT ms or s, the unit of its AppleScript track length (macOS) guessed
SPYTIFY_TARGET_LAUNCH a command that opens it the system’s own way
SPYTIFY_TAP the capture helper spytify-tap (macOS, Windows) next to spytifyd, then PATH
SPYTIFY_CONFIG the settings file config.toml in the configuration folder
SPYTIFY_SOCKET the recorder’s socket in the runtime or state folder
SPYTIFY_FFMPEG the ffmpeg binary ffmpeg on PATH, then next to spytifyd and in the usual install folders

Snap and Flatpak builds of an application can run under another process name, and expose MPRIS only through their sandbox. List every process name in SPYTIFY_TARGET_PROCESS. If spytify-cli info finds the process but no MPRIS name, check the sandbox’s D-Bus permissions (snap connections <snap>, flatpak info --show-permissions <app>).

Everything else is a setting. Clients of their own speak the protocol.

Protocol

Clients speak newline-delimited JSON-RPC 2.0, protocol version 1, on the recorder’s socket, and on TCP with spytifyd --listen 127.0.0.1:7811. Actions are requests; events are notifications sent to every client. Go clients import github.com/spytify/spytify/pkg/proto; the same contract is in pkg/proto/schema.json.

{"jsonrpc":"2.0","id":7,"method":"settings.set","params":{"values":{"encode.format":"flac"}}}
{"jsonrpc":"2.0","id":7,"result":{"applied":["encode.format"],"rejected":{}}}
{"jsonrpc":"2.0","method":"settings.changed","params":{"values":{"encode.format":"flac"}}}

Actions

Method Params Result Does
core.info none CoreInfoResult versions, resolved target and capability checks; first call on connect
session.start SessionStartParams SessionStartResult fails if the target process is not running or ffmpeg is missing
session.stop SessionStopParams SessionStopResult lets the current track finish, unless now; a track ended by now can be saved with track.keep
session.status none SessionStatusResult snapshot for late-joining clients
settings.get SettingsGetParams SettingsValues every setting when keys is empty
settings.set SettingsSetParams SettingsSetResult validated, persisted, then settings.changed fires
settings.reset SettingsResetParams Empty back to defaults, all keys or those under a prefix
player.control PlayerControlParams Empty play, pause, next, previous, restart (from 0:00) through the player’s local interface
player.launch none Empty opens the player if it is not running: SPYTIFY_TARGET_LAUNCH, else the OS’s way
log.tail LogTailParams LogTailResult last N log lines
events.recent none EventsRecentResult the last 200 events, for a client that connects mid-session
track.keep TrackKeepParams Empty saves a keepable take after all, as “ (partial)"; fails once it is no longer keepable
core.shutdown none Empty stops the session first

Events

Event Payload Sent
session.state SessionStateEvent every transition
track.started TrackStartedEvent boundary committed
track.finished TrackFinishedEvent file closed and tagged
track.skipped TrackSkippedEvent decision made: duplicate, too_short, ad, incomplete, seeked
track.keepable TrackKeepableEvent a take cut short, stopped or moved along its track bar can be saved with track.keep, or no longer
level LevelEvent 10 Hz while recording
settings.changed SettingsValues after any successful settings.set or settings.reset
target.state TargetStateEvent player process appears or exits
log LogEvent each log line, every level
error ErrorEvent capture, encoder or disk failure

Types

CoreInfoResult

Field Type
version string
protocol integer
target TargetInfo
platform string
checks array of Check
warnings array of string, optional

Empty

An empty object.

ErrorEvent

Field Type
code string
msg string
fatal boolean

EventsRecentResult

Field Type
events array of Event

LevelEvent

Field Type
peak_dbfs number
rms_dbfs number

LogEvent

Field Type
level string
msg string
ts string

LogTailParams

Field Type
lines integer

LogTailResult

Field Type
lines array of LogEvent

PlayerControlParams

Field Type
cmd string

SessionStartParams

Field Type
single_track boolean, optional

SessionStartResult

Field Type
session_id string

SessionStateEvent

Field Type
state string

SessionStatusResult

Field Type
state string
session_id string, optional
track Track, optional
counters Counters

SessionStopParams

Field Type
now boolean, optional

SessionStopResult

Field Type
recorded integer
skipped integer
stopping boolean, optional

SettingsGetParams

Field Type
keys array of string, optional

SettingsResetParams

Field Type
prefix string, optional

SettingsSetParams

Field Type
values object of any

SettingsSetResult

Field Type
applied array of string
rejected object of string

SettingsValues

Field Type
values object of any

TargetStateEvent

Field Type
running boolean
pid integer, optional

TrackFinishedEvent

Field Type
id string
counter integer, optional
path string
duration_ms integer
bytes integer
tagged boolean
tagged_by string, optional
timings FinishTimings, optional
partial boolean, optional

TrackKeepParams

Field Type
id string, optional

TrackKeepableEvent

Field Type
id string
keepable boolean

TrackSkippedEvent

Field Type
id string
reason string

TrackStartedEvent

Field Type
id string
artist string
title string
album string, optional
length_ms integer
cover_url string, optional
source AudioSource, optional
cut TrackCut, optional

AudioSource

Field Type
sample_rate integer
channels integer

Check

Field Type
name string
ok boolean
detail string, optional

Counters

Field Type
recorded integer
skipped integer
ads integer
bytes integer
overruns integer

Event

Field Type
method string
params any JSON

FinishTimings

Field Type
format string
trim_ms integer
cover_ms integer
encode_ms integer
tag_ms integer
total_ms integer

TargetInfo

Field Type
process string
mpris string, optional
bundle_id string, optional
launch string, optional

Track

Field Type
id string
artist string
title string
album string, optional
length_ms integer
cover_url string, optional

TrackCut

Field Type
kind string
by string
offset_ms integer