Documentation
¶
Overview ¶
Package streams reads AudD stream results (live longpoll events and the recent-results endpoint), records them into the local stream store, runs the background recorder, and relays plays to a local callback handler.
Index ¶
- Constants
- Variables
- func Backfill(ctx context.Context, a *app.App, store *streamstore.Store, radioIDs []int, ...) error
- func CallbackMissing(ctx context.Context, c *audd.Client) (bool, error)
- func Do[T any](ctx context.Context, a *app.App, call func(c *audd.Client) (T, error)) (T, error)
- func EnsureBackground(a *app.App) (started bool, err error)
- func EnsureCallbackURL(ctx context.Context, a *app.App) error
- func EnsureRecorder(a *app.App) (note string, err error)
- func Executable() (string, error)
- func FormatTimestamp(t time.Time) string
- func HealthCallbackBody(h streamstore.HealthEvent) []byte
- func HealthFromNotification(n audd.StreamCallbackNotification, radioID int, now time.Time) streamstore.HealthEvent
- func InstallService(profile string) (path string, err error)
- func IsNoCallback(err error) bool
- func LogPath(profile string) string
- func MapError(a *app.App, err error) error
- func OpenLog(profile string) (*os.File, error)
- func PIDPath(profile string) string
- func ParseRecentResults(body []byte, now time.Time) ([]streamstore.Play, error)
- func ParseTimestamp(s string) (t time.Time, ok bool)
- func PlayCallbackBody(p streamstore.Play) []byte
- func PlayFromMatch(m audd.StreamCallbackMatch, now time.Time) streamstore.Play
- func RecentResults(ctx context.Context, radioID int, category string) ([]streamstore.Play, error)
- func RestartBackground(a *app.App) (restarted bool, err error)
- func SetSpawnForTesting(f func(exe string, args, env []string, logPath string) (pid int, err error)) (restore func())
- func SetTerminateForTesting(f func(pid int) error) (restore func())
- func StartBackground(a *app.App) (started bool, err error)
- func Status(profile string) (running bool, pid int, since time.Time, err error)
- func Stop(profile string) error
- func StopBackground(profile string) (stopped bool, err error)
- func StopPath(profile string) string
- func WithStopFile(ctx context.Context, profile string) (context.Context, context.CancelFunc)
- type Forwarder
- type Lock
- type Recorder
- type RecorderOptions
- type Service
Constants ¶
const BackgroundChildFlag = "background-child"
BackgroundChildFlag is the hidden flag `audd streams record` gets when it runs as the background recorder.
const EmptyCallbackURL = "https://audd.tech/empty/"
EmptyCallbackURL is a placeholder callback URL that accepts and discards callbacks. Longpoll only delivers events when some callback URL is set.
const RecorderNote = "Recording stream results in the background. See audd streams recorder status."
RecorderNote is shown the first time audd starts the background recorder for a profile, by any command or screen.
Variables ¶
var ErrNotRunning = errors.New("no stream recorder is running")
ErrNotRunning is returned by Stop when no recorder runs for the profile.
var HTTPClient = &http.Client{Timeout: 30 * time.Second}
HTTPClient is used for the recent-results endpoint and for relaying plays to --forward-to handlers. It honors HTTPS_PROXY and NO_PROXY.
var LongpollHTTPClient = &http.Client{}
LongpollHTTPClient is used for longpoll requests, which carry no API token. It has no overall timeout: each poll sets its own deadline.
var RecentResultsURL = "https://api.audd.io/lastSong/getChannelById/"
RecentResultsURL is the recent-results endpoint: the last ~30 results per stream, keyed by longpoll category. It needs no API token.
Functions ¶
func Backfill ¶
func Backfill(ctx context.Context, a *app.App, store *streamstore.Store, radioIDs []int, freshWithin time.Duration) error
Backfill stores the recent results of the account's streams (or only the given ones) once, without longpolling, and records the account's stream list. Streams a recorder covered within freshWithin are skipped (0: none are). Commands that read the store use it when it is not current.
func CallbackMissing ¶
CallbackMissing reports whether the account has no callback URL (AudD error 19 from getCallbackUrl), which stops longpoll from delivering events. Other errors come back as the SDK returned them.
func Do ¶
Do runs a stream API call through api.Do, so a rejected token fetched by audd login is healed and retried once, and errors get the same codes, exit codes, and token hints as every other command (see MapError).
func EnsureBackground ¶
EnsureBackground starts the background recorder for the active profile unless it is running, turned off (streams.background_recorder false or AUDD_NO_BACKGROUND_RECORDER set), or there is no API token. started reports whether this call started it. It is assigned to app.EnsureRecorder. AUDD_NO_BACKGROUND_RECORDER is intentionally supported but left out of the user docs: tests (internal/e2e) and throwaway environments such as CI use it so that only an explicit `audd streams recorder start` starts a recorder.
func EnsureCallbackURL ¶
EnsureCallbackURL makes sure longpoll can deliver events: when the account has no callback URL it offers to set EmptyCallbackURL, and sets it only after confirmation (or --yes). It never changes an existing callback URL.
func EnsureRecorder ¶
EnsureRecorder starts the background recorder when it should run (through app.EnsureRecorder) and returns the note to show: RecorderNote when this call started the recorder and the profile has not seen the note before, else "". err is the start error, which callers treat as a note at most.
func Executable ¶
Executable is the path services and the background recorder run: the audd on PATH when it is this binary (so package-manager upgrades keep working), else this binary.
func FormatTimestamp ¶
FormatTimestamp writes a time the way AudD stream results do.
func HealthCallbackBody ¶
func HealthCallbackBody(h streamstore.HealthEvent) []byte
HealthCallbackBody builds the callback JSON for a health event.
func HealthFromNotification ¶
func HealthFromNotification(n audd.StreamCallbackNotification, radioID int, now time.Time) streamstore.HealthEvent
HealthFromNotification converts a stream notification into a HealthEvent.
func InstallService ¶
InstallService writes the service file for this OS and returns its path. It does not activate it; ServiceFor(profile).Activate is the command that does.
func IsNoCallback ¶
IsNoCallback reports whether err is AudD error 19 meaning the account has no callback URL.
func MapError ¶
MapError turns an audd-go error into an *output.Error the way api.MapError does (with a hint that fits where the token came from), plus the streams docs link and a stream-monitoring hint when it is not enabled. Other errors pass through with any token in their text redacted (Go's connection errors include the request URL, which carries the token on some API calls).
func ParseRecentResults ¶
ParseRecentResults parses a recent-results response body.
func ParseTimestamp ¶
ParseTimestamp reads a stream-result timestamp: "YYYY-MM-DD hh:mm:ss" in AudD's UTC+3, RFC 3339, or Unix seconds/milliseconds. ok is false when the value is not a time.
func PlayCallbackBody ¶
func PlayCallbackBody(p streamstore.Play) []byte
PlayCallbackBody builds the callback JSON for a play (used for plays that came from the recent-results endpoint rather than a live event).
func PlayFromMatch ¶
func PlayFromMatch(m audd.StreamCallbackMatch, now time.Time) streamstore.Play
PlayFromMatch converts a longpoll/callback recognition into a Play. now is used when the event carries no usable timestamp.
func RecentResults ¶
RecentResults returns a stream's recent results, oldest first. The endpoint is undocumented, so the response is read leniently: unknown fields are ignored, wrong-typed values degrade to empty, and entries without an artist or title are skipped. RadioID is left at 0 unless the entry carries one; callers set it. Failures are network or server errors (exit 5, retryable) that name the stream.
func RestartBackground ¶
RestartBackground restarts the profile's background recorder, if one runs, so it reads the API token again (after audd token rotate, say). A recorder run in the foreground with audd streams record is left alone; it picks up a stored token change within five minutes.
func SetSpawnForTesting ¶
func SetSpawnForTesting(f func(exe string, args, env []string, logPath string) (pid int, err error)) (restore func())
SetSpawnForTesting replaces how the background recorder process is started and returns a function that restores the default.
func SetTerminateForTesting ¶
SetTerminateForTesting replaces how a recorder process is asked to exit and returns a function that restores the default.
func StartBackground ¶
StartBackground starts the background recorder for the active profile even when automatic starting is turned off. started is false when one is already running.
func StopBackground ¶
StopBackground stops the profile's background recorder, if one runs (after audd logout, say, when it used the token from the sign-in). A recorder run in the foreground with audd streams record is left alone. stopped reports whether one was stopped.
func StopPath ¶
StopPath is the file `audd streams recorder stop` writes to ask a running recorder to shut down. Windows has no SIGTERM for detached processes, so this file is how every platform asks for a clean exit.
func WithStopFile ¶
WithStopFile returns a context that is cancelled when the profile's stop file appears. It removes any stale stop file first, and removes the file again when the returned cancel function runs.
Types ¶
type Forwarder ¶
Forwarder relays stream events to a local handler as callback-shaped POSTs, the same JSON AudD sends to a callback URL.
type Lock ¶
type Lock struct {
// contains filtered or unexported fields
}
Lock is the single-recorder-per-profile lock.
func Acquire ¶
Acquire takes the recorder lock for a profile and writes the PID file. It fails with recorder_running when another recorder holds it.
func (*Lock) Check ¶
Check reports why this recorder no longer owns the profile: its lock file or PID file was removed or replaced (by hand, or by cleaning the data directory). Without them, status cannot see the recorder, stop cannot stop it, and a second recorder could start, so a recorder that gets an error here should exit. It returns nil while both are in place.
func (*Lock) MarkBackground ¶
MarkBackground records in the PID file that this is the background recorder, which RestartBackground may restart.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder longpolls an account's streams and stores every play and health event in the stream store.
func NewRecorder ¶
func NewRecorder(a *app.App, store *streamstore.Store, opts RecorderOptions) *Recorder
NewRecorder returns a recorder that writes into store.
func (*Recorder) Run ¶
Run records until ctx is cancelled. It backfills each stream from the recent-results endpoint, then longpolls every stream, re-reads the stream list and the API token every 5 minutes, writes a heartbeat every 30 seconds for each connected stream, and reconnects with backoff after errors. When AudD refuses the stream list (a rejected token, no stream monitoring on the account), recording is paused, the problem is noted in the store's last_error, and each rescan tries again. A rejected token fetched by audd login is replaced with the account's current one first. Run returns nil when ctx ends, or an error when there is no API token or AudD rejects a token given with --token or AUDD_API_TOKEN (it cannot change while this process runs; the problem is noted all the same).
type RecorderOptions ¶
type RecorderOptions struct {
// RadioIDs limits recording to these streams; empty means every stream
// on the account, including ones added later.
RadioIDs []int
// ForwardTo, when set, receives each live play and health event as a
// callback-shaped POST.
ForwardTo string
// OnPlay is called once for every play this recorder sees after its
// start-up backfill: live plays, and plays a reconnect backfill finds.
// A play is reported even when another recorder writing the same store
// saved it first. It may be called from several goroutines at once.
OnPlay func(streamstore.Play)
// OnHealth is called for every health event.
OnHealth func(streamstore.HealthEvent)
// Logf receives connection problems and other notes (default: discard).
Logf func(format string, args ...any)
// Owned, when set, is checked with every heartbeat: an error means
// this recorder no longer owns its profile (see Lock.Check), and Run
// stops, logging why.
Owned func() error
}
RecorderOptions configure a Recorder.
type Service ¶
type Service struct {
Path string // where the file goes
Content string // file contents (UTF-8; Windows task XML is written as UTF-16)
Activate string // the command that turns it on
// StartsNow reports whether Activate also starts the recorder right
// away (systemd and launchd); a Windows scheduled task first runs at
// the next logon.
StartsNow bool
}
Service describes the login service file for a profile's recorder.
func ServiceFor ¶
ServiceFor returns the service definition for this OS: a systemd user unit on Linux and other Unix systems, a launchd agent on macOS, and a Task Scheduler task on Windows. Each runs `audd streams record` at login.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package streamstest is a fake AudD stream API for tests: stream management methods, longpoll, and the recent-results endpoint.
|
Package streamstest is a fake AudD stream API for tests: stream management methods, longpoll, and the recent-results endpoint. |