Repository navigation
Releases
Spotify 0.4.0 — No telemetry
Spotify 0.4.0 removes app telemetry from the CLI, helper, website and backend, including all Space Station reporting.
Collectors, event queues, relay endpoints, telemetry settings and website tracking controls are gone. Upgrading the helper deletes queued telemetry while preserving functional app data. Accounts requests explicitly opt out of telemetry; functional client requests opt out when talking to older app backends.
This is a breaking Rust client release: telemetry APIs and related configuration fields have been removed. Explicit user-submitted bug reports and local operational logs remain available.
Downloads cover macOS, Linux and Windows on ARM64 and x86_64. macOS releases are Developer ID signed and Apple notarized. Verify downloads with SHA256SUMS. Silicon Apps currently serves the two Linux targets; use these native downloads for macOS and Windows.
Spotify 0.3.0 — Silicon Accounts and Apps
Spotify 0.3.0 uses Silicon Accounts for Carbon and Silicon sign-in and Silicon Apps for package delivery and network updates. Browser and CLI sessions persist across restarts, refresh safely, and end on expiry or logout. Ownership follows the account UUID when a public handle changes.
The website uses Silicon UI. Organization and testing-environment support and test suites have been removed.
Native downloads cover macOS, Linux and Windows on ARM64 and x86_64. macOS executables are Developer ID signed and Apple notarized. SHA256SUMS verifies every download. Spotify desktop playback requires macOS.
Silicon Apps currently accepts Linux packages; use native downloads for macOS and Windows until those store validation workers are available. Ting notifications require an Accounts Proof-compatible Ting deployment; pending deliveries are retained through upstream outages. Existing legacy sessions require a new Silicon Accounts login.
Spotify 0.2.0 — IAM 5
Spotify 0.2.0 migrates the CLI, daemon and production backend to IAM 5.2.1.
- Sign-in selects one Carbon or Silicon account and organization. Notification permission is requested separately through an explicit IAM consent flow.
- Ting credentials are encrypted and saved independently of ordinary sessions, with safe refresh and retry handling. Scheduled triggers keep their original account and organization.
- Saved contexts can be selected explicitly, and signing out leaves that context unselected.
Existing IAM 4 sessions may require a fresh sign-in and separate Ting permission approval.
Includes CLI and daemon packages for macOS, Linux and Windows on ARM64 and x86_64, plus the Honeycomb package and installer. macOS executables are Developer ID signed and notarized. Artifact hashes are in SHA256SUMS.
Validation: client, daemon and backend tests; strict Clippy; six-platform builds; Honeycomb package validation; production health and readiness checks.
v0.1.7 — API-first managed queues
- Managed-queue transitions,
nextand resuming the interrupted context now use the same
API-first playback controller asspotify play. Underauto, AppleScript runs only when
the API cannot start playback, so queued songs no longer switch focus to Spotify each time. - Queue starts honor the configured strategy, record verified playback immediately, and
recognize relinked songs. Slow API requests no longer expire a hand-off while it is running. - Managed
next --jsonreports the actual playback method, fallback reason and playback state;
automatic transitions log the same routing information. - Saved local files and legacy collection contexts retain their AppleScript resume path when
the configured strategy permits it; a failed resume no longer discards its saved point.
spotify-cli v0.1.6
Playback control:
- Every start goes through the Spotify Web API first, so Spotify.app stays in the background:
songs, episodes, shows and Liked Songs directly (new), albums, playlists, artists and radios
through spotify_player (as before). Starts fall back to AppleScript, except a radio, which has
no AppleScript way; only exact seeks go to AppleScript first. The outcome says
via: "web_api"(new) once Spotify.app plays the item. Before, songs, episodes, shows and
Liked Songs started through AppleScript, which brought Spotify.app to the front. spotify play <track|episode>(alsoplay --search,podcast playandsearch --playwhen
they pick a song or episode, and a song with--context) plays in--contextwhen given, else
in the song's album or the episode's show (looked up withGET /v1/tracks/{id}or
/v1/episodes/{id}in your market), from the start. The rest of the album then plays on, and
next/previousmove through it. A relinked song starts from the release that plays in your
market. A start that lands on another album track, or whose uri the album refuses, is sent
again once by the track's position (counted across discs). Lookups are remembered for the life
of the daemon (up to 500 items).spotify play spotify:show:<id>starts the show as its own list.spotify play --likedstarts the Liked Songs list itself at its first song, or with--random
at a random position below its size (GET /v1/me/tracks?limit=1). Once it plays, its shuffle
is set (PUT /v1/me/player/shuffle, or AppleScript'sset shufflingwhen that does not show),
and in order it starts again at the first song when the list began elsewhere. An empty Liked
Songs isnot_foundat once, and nothing starts. AppleScript remains the fallback, with the
focus handed back.spotify play --liked --shuffledoes the same as--liked --random.- Where a start plays: the devices are listed at every start (
GET /v1/me/player/devices). When
another device is active (a speaker or phone Spotify.app controls; never spotify_player's), the
start plays there instead of moving playback to this Mac, and the outcome's newnotesays so.
When Spotify.app plays on a device the Web API does not list, the start names no device.
Otherwise it goes to Spotify.app on this Mac: the Computer device named like this Mac
(scutil --get ComputerName), or, only when that name is unknown, the only Computer device;
another Mac and the Web Player are never picked. - A start answered with a server error (5xx), or not answered in time, is looked for in
Spotify.app before any fallback. When it plays, it counts (via: web_api, with anote) and
is not started a second time. - When the Web API start cannot run or does not take effect within
verify_timeout_ms,
AppleScript starts the item, withfallback: {"from": "web_api", "reason": …}:
rate_limited(a 429, or the pause after one),premium_required,device_restricted(new:
the active device takes no Web API commands),device_not_found(new),no_active_device,
unsupported(not playable in your market; nothing is sent),
spotify_auth_required/spotify_player_missing,web_api_failed(new),not_found,
transportorno_effect. When AppleScript fails too, the Web API attempt is in
details.first_attempt. The AppleScript fallback plays a song in the album the lookup found
(from the release that plays in your market), so the album goes on after it, unless
--contextwas given or the album did not place the song. Strategyapplescriptnever uses
the Web API; under strategyspotify_player, episodes, shows and songs with--contextcan
now start (through the Web API). - When an AppleScript start brings Spotify.app to the front (a fallback, an album or playlist
spotify_player could not start, the restore after a failed start, and the managed queue's
hand-offs and resumes), the focus goes back to the app that had it, and Spotify.app is hidden
again if it was hidden. The front is watched during the start and for 1 s after it;
lsappinfo setfrontis tried, thenopen -afor regular apps that still run (never Finder).
No new macOS permission is needed. The outcome carries
refocused: {app, via: "setfront"|"open", hid_spotify?, error?}(details.refocusedon
errors);focus_not_returned(new) appears only inside it. Measured: Spotify.app in front for
about 0.5 s instead of staying there. - The Web API rate-limit pause (
Retry-After, 1 s to 10 minutes, 30 s without one) is one
process-wide pause, shared by the daemon's lookups, searches and starts. - The
automation_permission_deniedhint starts with the exact place: System Settings →
Privacy & Security → Automation → spotify-daemon → turn on Spotify. play --likedwaits for Spotify to apply Liked Songs' saved shuffle setting (it switches a
moment after the first song starts) before setting the order you asked for, and verifies it:
plain--likedalways starts in order at the first liked song,--randomat a random song.- A song or episode Spotify will not play for this account (
details.reason:market,
explicitorproduct) fails at once with the newnot_playable(exit 1) and leaves playback
alone; before, the AppleScript attempt emptied Spotify.app for seconds first. - A relinked song starts in its album with one request, named by the id the album lists, so the
album's first song no longer plays for a moment.
Search and queue:
- When spotify_player's search fails (such as its "invalid type: null, expected a boolean" parse
error),search,podcast searchandplay --searchfall back to the Web API's own
GET /v1/search. Replies sayvia(spotify_playerorweb_api, new) and carry
fallback: {from: "spotify_player", reason}when the Web API answered; when both fail, the
error is the Web API's withdetails.first_attempt. spotify_player errors that end in a JSON
parse position (line 1 column 42912) are no longer mistaken for HTTP 429 or 401. spotify queue/queue listJSON has a new top-levelwarningsarray. It holds
no_active_devicewhen Spotify reports nothing playing on any device and Spotify.app does not
play either, so Spotify's upcoming list is empty for that reason. The human output prints each
warning asnote: <message> (<code>), with its hint on the next line.queue listhas the aliasesqueue showandqueue ls.
Lyrics:
spotify lyrics <uri|link|id>shows any track's lyrics, whether or not anything plays, and with
a target Spotify.app need not run;spotify lyricsalone is the song playing now. It takes a
track, not search words: to find a song by name, runspotify search '<words>' --type track,
thenspotify lyrics <uri>. One word that is no id (spotify lyrics 505) isinvalid_input
("505is not a track URI, link or id.") with a hint namingspotify search 505 --type track
anddetails.search; several words are a usage error whose hint names
spotify search '<words>' --type track.
Finding commands:
spotifywith no arguments prints an orientation instead of the help (exit 0, was 2): what is
set up and what is missing, each with its fix, what is playing, and up to five things to try
(such asspotify lyrics spotify:track:0BxE4FqsDD1Ot4YuBXwAPpwhen nothing plays). Local
checks only; the daemon is asked only when it already runs, never started.--jsongives
{version, ready, now_playing, checks, try, help}.spotify --helplists the commands grouped by goal (Listen, Find, Lyrics & details, Queue,
Playlists, Podcasts, Triggers (Ting), Account & login, Setup & diagnose), each with a summary
and an example. Most summaries were rewritten to say what the command can do, and every
command's--helpnow ends with Examples. A command's own flags are underOptions:, followed
byGlobal options:(--json,--org,--api-url,-h/--help,-V/--version); the root
help's heading isGlobal options (every command takes them):.-Von a subcommand prints
spotify <version>(wasspotify-queue …).- New
spotify how "<question>"[--limit 1-10] [--json]: answers offline with the best
commands and ready-to-run examples, the guide section to read and matching error codes. It
indexes the help, the guides, every setting and the error codes. A song or artist name in the
question never outweighs what you want to do, and it becomes made examples: lyrics get
spotify search '<name>' --type trackthenspotify lyrics <uri>, play, queue add and search
--search '<name>'; settings questions getspotify config set '{"<key>": <value>}',
completion questions both install lines, docs questionsspotify docs --search '<flag>'.
Questions that only ask to see something put read-only commands first. JSON:
{question, name, terms, commands, guides, errors, more}. An empty question is
invalid_input. Next:suggestions after human output, on stderr, with the real URIs and ids just printed
(search, podcast search, status, the play commands, track, lyrics, library, queue, queue add,
playlist list/show/create/fork, devices, trigger add/list, login, login status, doctor, config
set, how). Never with--json; the new environment variableSPOTIFY_HINTS=0turns them off.
They replace the search footer on stdout ("Play one: … · queue it: … · look inside: …"), and the
hints aftertrigger addandloginchanged to match.spotify commands --jsonkeeps every field and adds, per command,path,summary,
details,goal,goal_title,capabilities,keywords,subcommands,arg_groups,
examples,output,errors,requirements,requirement_notes,help,mutates(true
when it can change something or send something out),changes(fromplayback,library,
playlists,config,triggers,daemon,session) andread_only_when(e.g.volume
...
spotify-cli v0.1.5
Signing (macOS):
- Release binaries are signed with the project's Developer ID (team
LTBSK59BJ2) under the
hardened runtime, with a secure timestamp, and notarized by Apple. macOS keeps the Automation
answer ("spotify-daemon" may control "Spotify") by that signature, so updates no longer ask
again; the first signed release asks once more after the ad-hoc signed 0.1.4. Gatekeeper
accepts a binary downloaded with a browser (bare executables cannot be stapled, so it looks the
ticket up online on first launch). The codesign identifiers stay
com.unlikefraction.spotifyandcom.unlikefraction.spotify-daemon. spotify-daemonembeds an Info.plist (__TEXT,__info_plist: bundle identifier
com.unlikefraction.spotify-daemon, name, version, andNSAppleEventsUsageDescription, the
reason macOS shows in the permission prompt), and signed releases give it the
com.apple.security.automation.apple-eventsentitlement the hardened runtime requires for its
Apple Events. Thespotifycommand sends no Apple Events, so it gets neither. Linux and
Windows builds are unchanged.scripts/package-release.shsigns each macOS architecture through the new
scripts/macos-sign.shbefore packing:SPOTIFY_CODESIGN_IDENTITYfor Developer ID (unset:
ad-hoc, as before, for CI and contributors) andSPOTIFY_NOTARY_PROFILE(a
notarytool store-credentialskeychain profile) for notarization. It verifies every signature
(identifier, runtime flag, timestamp, team, the daemon's Info.plist and exact entitlements),
requires notarization statusAcceptedwith a ticket that lists both binaries' cdhashes
(printing the notary log otherwise), checks the identity (exactly one match, and a Developer
ID Application one when notarizing) and the profile before the build starts, and refuses a
notary profile without an identity.
spotify-cli v0.1.4
Daemon and its spotify_player:
- The warm spotify_player re-reads playback every 20 s instead of every 3 s. The 3 s poll (10
GETs per 30-second window) kept the owner's own client ID rate-limited even while idle: a 429
about every 30 s, eachRetry-After(6–15 s) freezing spotify_player's view and stalling
commands for that long (spotify trackandqueue add --searchtook about 9 s,likerefused
a song the view had not caught up with). 20 s is 1–2 GETs per window, so commands keep most of
the quota. A positiveplayback_refresh_duration_in_msin yourapp.tomlis now kept when it
is slower, and raised to 10 s when faster.spotify daemon statusreads
"playback refresh every 20 s" (refresh_ms: 20000). The first daemon start after the update
replaces the 0.1.3 copy. - spotify_player's view can now be up to about 20 s behind a change made in Spotify.app. Commands
that depend on it already compare it with Spotify.app first. The queue's resume point, which
took the interrupted context from that view unchecked, now reads the Web API once when the view
names another item than the Web API's queue. A list started moments before the queue
interrupts it is now the one resumed, not the list before it.
Playback control:
- Relinked songs (Spotify plays the same recording from another release under another id, so
Spotify.app and the Web API name different ids) are recognised: by the Web API item's
linked_from, or by the same title, a length within 1 s and the same album name.
spotify status --fullcounts such a song as the current item:web.relinked: true(new,
present only when true), noweb.staleand noweb_state_stalewarning, served from
spotify_player's memory instead of a fresh Web API read on every call. Before, it was stale
forever, with a hint to retry in a few seconds. The checks before spotify_player's relative
seek,nextandprevious, and the context kept to restore after a failed start, also count
it as Spotify.app's song. likeandunlikeon a relinked song refuse at once, without the volume nudge and the wait,
withtrack_mismatchthat is now not retryable: spotify_player would save or remove the
substitute's id, not the one Spotify.app shows.detailsaddrelinked: trueandmatched_by
(linked_fromortitle_length_album); the hint points to the heart in Spotify.app.- The
web_state_stalehint for another item now says the Web API usually catches up within
seconds, and that if it keeps reporting another item,webdescribes that item. play --likedwithout--randomplays Liked Songs in list order from its first song. A
shuffle Spotify kept for Liked Songs (from an earlier--random, or set in Spotify.app) is
turned off, and stays off, and the list is started again; before, it played shuffled and
always from the same song.play --liked --randomswitches Liked Songs' shuffle off and on, so Spotify draws a new order,
then skips once, so it starts at a random song every time; before, a kept shuffle started on
the same song again and again.- Each of those steps is checked in Spotify.app: one that does not show is a retryable
verification_failednaming the step (Liked Songs keeps playing), never a success. A Liked
Songs start that does not show within 2.5 s fails then (with--randomit took about 5 s, and
a start that showed late counted as a success without its shuffle step).
Queue:
- Queued episodes get their name, show and length more reliably. What the caller knows (the
search hit ofqueue add --search) is kept field by field and the rest is looked up, so an
episode picked with--searchgets its show; an empty name or show counts as missing. - Facts a lookup did not get are looked up again: after a rate limit, in the background once
Retry-Afterhas passed (for up to 10 minutes), and after other failures up to 3 tries per
item;spotify queuealso looks them up itself for up to 2.5 s. Before, an episode queued
while Spotify was rate-limiting stayedepisode <id>for good. When a lookup is left to the
background, thequeue addreply carriesmetadata_pending(the queued item ids) and a
note. - The
spotify queuenote on repeats of the item playing now names its kind ("the track
playing now") and gives a cause (repeat-one, no context, or nothing else up next in its album
or playlist) only when spotify_player's view of that item shows it and Spotify.app's repeat flag
agrees; otherwise it names none. Before, it blamed an episode without a context or repeat-one,
also for a track at the end of its album with repeat off.
spotify-cli v0.1.3
Playback control:
likeandunlikenever change the wrong song. spotify_player can only like the track it
believes is playing, which could be an earlier one, so they now check that it is the song
Spotify.app plays. If it is not, they nudge it once (an inaudibleplayback volume <current>)
and wait up toverify_timeout_ms+ 1.1 s. If it still does not match, they refuse with the new
track_mismatchand change nothing (before,likecould save the previous song, or report
success for a podcast episode and do nothing). Episodes, ads and local files are refused as
unsupported.- Before a spotify_player command whose effect depends on its own memory (play, pause, toggle,
shuffle, next, previous, seek), that memory is compared with Spotify.app. When they disagree,
AppleScript acts at once andfallback.reason.codeis the newstate_mismatch; under strategy
spotify_playerthat is the error.togglesends an explicit pause or play chosen from
Spotify.app's state. - Seeks are exact: AppleScript sets the position first; spotify_player's relative seek is only
the fallback, and under strategyspotify_playerit is refused withstate_mismatchwhen
spotify_player is on another item.play <track|episode|show>goes to AppleScript only, so a
start never leaves Spotify.app stopped with nothing loaded. play --likedplays the Liked Songs list itself through AppleScript, sonextandprevious
stay in it; it no longer stops playback.--randomturns shuffle on for Liked Songs (Spotify
keeps it) and starts at a random song;--limitapplies only when spotify_player starts the
list. A failed start that leaves Spotify.app empty puts back what was playing, under any
strategy (details.restored, ordetails.restore_failed).- Refusals the running spotify_player logs (429, 403) are noticed at once instead of after the
verify timeout:fallback.reasonisrate_limited,no_effectorno_active_device, with
details.requestanddetails.spotify_player.play,pause,toggle,volumeand
shufflewait at most 1.5 s before falling back. With the state checks and AppleScript-first
seeks,toggle,seekandpreviousno longer take 3.6–6.8 s when spotify_player's command
would have no effect. previousbehaves the same on every path: from 3 s in, or when there is no item before it,
it restarts the item; otherwise it goes to the previous item. The outcome's newresult
(restartedorprevious_item) and the first line of the human output ("Back to the start of
this item." / "Back to the previous item.") say which.repeatfollows spotify_player's real order (off → track → context), confirms each step and
checks the result in Spotify.app. It no longer reports success while leaving another mode, and
arepeat offit cannot confirm never passes through repeat-one. When a refused step leaves
repeat-one on, it returns that failure instead of an AppleScript success that leaves the song
repeating. When spotify_player refuses for now,repeat trackreturns its error (e.g.
rate_limited, retryable) instead ofunsupported.- AppleScript volume lands on the requested level (it was one below for most levels); 19, 39,
59, 79 and 99 land one above. Verification is exact. spotify status --full:webgainsitem_uri,is_playing,source(spotify_playeror
web_api) andstale. When spotify_player's memory disagrees with Spotify.app, a fresh Web API
read (1–4 s) is used instead; when even that is for another item,web.staleis true, repeat
and shuffle that contradict Spotify.app are left out, and the new warningweb_state_stale
says so. New warnings also includeno_active_deviceandtimeout. The human output then
takes repeat from Spotify.app and adds "web data out of date".- Time left,
(-m:ss), inspotify statusandspotify trackis rounded to the nearest
second, as isplayback.remainingin trigger firings: a--remaining 20sfiring reads 0:20,
not 0:19 (remaining_msis unchanged). spotify launch --jsonanswerslaunched: false, already_running: truewhen Spotify.app was
already running (it saidlaunched: true) and does not runopen; the human output says
"Spotify.app was already running." or "Started Spotify.app (hidden).".
Queue:
- Quick or concurrent
spotify nexts are applied one after another and never hand off the same
item or lose one. Anextthat arrives while the previous hand-off is still in flight skips
that item and plays the one after it (skippedin the reply, and a
skipped <uri> (it was still being switched to)line). play <item>(also--likedand radios) andpreviouswait for anextor queue hand-off in
progress, and those wait for them, so changes reach Spotify in the order they were asked. A
failedplay <item>keeps the queue's resume point; if the managed item still plays, it stays
the managed item.- Episodes queued by URI get their name, show and length (a Web API lookup with
spotify_player's cached token), andqueue add --searchpasses the chosen hit's facts to the
daemon (known). Items without a known name show asepisode <id>/track <id>, Spotify's
upcoming items show their artists or show, andqueue addprints what it added
("Queued 1 item. Managed queue now has 3." plus a+ name — byline per item). Albums,
artists, playlists and shows are refused as "is an album" (and so on), with a hint that fits. spotify queueleaves out Spotify's repeats of the item playing now (an episode played without
a context was listed ten times) and prints a note saying so (current_repeats_left_out,
notein--json).- Resuming the interrupted list after the managed queue drains also works when its next item is
a podcast episode (its URI was built as a track's).
Details, library and playlists:
spotify trackshowsliked: yes|nofor songs (likedin--json, from a Web API check made
alongside the lookup, left out when it fails or takes over 2.5 s).spotify track <playlist> --jsonhas the same envelope as the other kinds:kind,item,
playlist,owner,collaborative,track_count,duration_ms,duration,tracks,raw.spotify track --typeoffers onlytrack,album,artistandplaylist;showor
episodeis a usage error. A bare id that is not found as a track gets a hint to pass
--type album|artist|playlistor a full URI.- Library and playlist reads (
library <section>,playlist list,playlist show,
track <uri>) are retried once by the daemon after a transienttransportor
spotify_player_busyerror, andlibrary,playlist listandplaylist showonce more by the
CLI. Rate limits, timeouts and writes are not retried. - A library or playlist read right after a playlist change or like/unlike waits until it can see
the change (1.1 s), also when another identical read was in flight during the change, so
playlist listright afterplaylist deleteno longer lists the deleted playlist.
Daemon and its spotify_player:
- The warm spotify_player is the daemon's own and actually serves the CLI: copies left by earlier
daemons (the pid in the newwarm-player.json, or orphans with the daemon's exact overrides)
are stopped before a start; it is reportedrunningonly once it holds the client port;
another spotify_player holding the port (your own TUI) is left alone and reported as
deferredwith its pid; a copy that never gets the port is stopped and retried
(not_serving). It dies with the daemon, even on SIGKILL. - The warm copy refreshes playback every 3 s (
playback_refresh_duration_in_ms; a smaller
positive value in yourapp.tomlis kept), about 20 small Web API requests a minute, so its
view is rarely more than 3 s behind Spotify.app. spotify daemon statusdescribes the warm spotify_player in words (pid and port, refresh
interval, who holds the port, retry delays, errors).spotify doctor's
spotify_player_warm_instancecheck passes only when the daemon's own copy holds the port, and
shows the state and the pid that holds the port when it fails.spotify daemon restartwith the launchd agent loaded restarts the job in place
(launchctl kickstart -k) and waits for a new pid; it used to stop the daemon and never start
it again. It prints "spotify-daemon restarted via launchd (pid N, vX).".daemon stopand
uninstallwait until launchd has unloaded the job;daemon startwaits up to 15 s and keeps
retrying while a previous instance is still exiting.spotify update --checkreports the realauto_updatesetting and no longer leaves
daemon statussaying it is off.- The telemetry relay drains the whole backlog (up to 2 000 events) every minute instead of 40
events a minute.
CLI:
- Human output keeps Spotify names on one line (line breaks, tabs and other control characters
become spaces);--jsonkeeps the original strings. While a podcast episode plays, the status
header has no dangling " — ", andspotify trackshowsshow:instead ofalbum:. - The
spotify searchfooter suggestsqueue it: spotify queue add <uri>only for tracks and
episodes, andlook inside: spotify track <uri>for albums, artists and playlists. spotify report --attachtakes text files only: binary files, directories and a home's
session.jsonortesting.jsonare refused withinvalid_inputbefore anything is sent. Of
long files only the last 64 KiB are sent, starting at a whole character.- Every
usageerror hasdetails.usage, anddetails.argumentanddetails.valuewhen the
parser names them; the hint always includes the usage line.--limit -1is
invalid_input("--limit -1 is out of range: it takes 1 to 10.") instead of the misleading
-- -1tip, and a hyphenated value after an option (--note -x) gets the tip to write
--note=-x. - `spotify config set searc...
spotify-cli v0.1.2
CLI:
spotify search queenand other searches whose results hold a line break work again:
spotify_player output with raw control characters inside strings now parses. When output still
cannot be parsed,spotify_player_failedsays whether it was malformed JSON or plain text, with
the command, stdout and parse error indetails.spotify track <album>shows the album type, release date, track count, length and track list,
andspotify track <artist>its top tracks, albums and singles and related artists, instead of
an empty template.--jsonaddskind,itemand those lists next toraw; album items gain
album_type.- Spotify references are checked before anything is sent to Spotify: ids must be 22 letters and
digits, and playlist commands take only playlists. Anything else isinvalid_input(exit 2)
with the accepted forms in the hint.playlist add/removecheck every item first, so an edit
never stops half way (episodes areunsupported). Spotify's "400 Bad Request" is
invalid_inputinstead ofspotify_player_failed. - Limits that cannot be honoured are refused instead of quietly changed:
search --limitand
podcast search --limittake 1–10 (spotify_player returns at most 10 per kind), as does the
search_limitsetting (a larger value saved by an earlier release is applied as 10, with a note);
library --limitandplaylist list --limittake 1 or more;trigger history --limit1–500. spotify trigger addrejects--times 0, a note over 1000 or a label over 80 characters, a
percentage outside 0–100% and a--trackthat is not a track or episode asinvalid_input
(exit 2) before reading Spotify, whatever is playing (was an Automation or Spotify error).spotify config seterrors name the key and what it accepts, with a copyable example, e.g.
verify_timeout_ms must be an integer from 200 to 15000, not "fast".Setting one key no longer
fails because another saved key is out of range.- Usage errors keep clap's details in
--json: the missing arguments,Possible values: …
(details.possible_values), tips and the usage line (details.usage). spotify queue add --typeoffers onlytrackandepisode.- New
spotify playlist fork --name <NAME>.playlist create --jsonreturns the bareidand
uri(spotify_player 0.25 gavespotify:playlist:spotify:playlist:…);playlist fork --json
also returns the new playlist'sid,uriandname.playlist createwithout
--description, or with a name starting with-, works. spotify doctorasks the backend for its version and adds the required check
cli_version_supportedwhen the backend names amin_cli.playlist showsays "1 track" / "N tracks". Help examples fit in 100 columns, so the
spotify loginpipe example andspotify report --prno longer wrap mid-command.
Daemon:
- The macOS Automation state now comes from what Apple Events actually do.
automation_permission_pendingis gone: while macOS's dialog is open, commands fail with
timeoutwhose hint names the dialog, and after one timeout the daemon fails fast for 3 s,
including commands already waiting, instead of stacking up. A reading that finds Spotify closed
no longer counts asgranted;spotify doctorshows anunknownstate as optional with a fix,
and waits out the 3 s window so it really re-checks. In the daemon's first two minutes a closed
Spotify is polled every 3 s until the first Apple Event reaches it. A denied permission no longer
waits 20 s for a launching Spotify. - Telemetry is relayed in requests under the backend's 64 KiB limit instead of 40-event batches
that could exceed it and be dropped. - Daemon ops refuse search and history limits they cannot honour, for every client, and
track.inforeturns the structured album/artist view itself.
Backend:
- Every error, including 405
method_not_allowedand 413payload_too_large(bodies over
512 KiB), is the JSON envelope with anX-Request-Idand a non-empty hint. POST /api/v1/auth/logoutanswers 204 for tokens IAM does not recognise (was 503).- A refused SLT is 401
slt_rejectedwith advice about the SLT; a refused refresh token is 401
unauthenticated; a plain IAM 400 isinvalid_input(was 503dependency_unavailable). - Telemetry batches over 64 KiB get 413
payload_too_largeinstead of being dropped silently. - A malformed bearer is 401
unauthenticatedbeforeX-Org-IDis checked. - IAM webhook deliveries that fail verification get
webhook_unverified, and the reason is
logged.
Installer and website:
- The installer adds its PATH line under
# spotify-cliunless that exact line is already there
(older# silicon-spotifyblocks count); another install directory gets its own line. Its closing
lines ask you to click Allow only while Automation is not granted, and print the System
Settings path when it was denied. The Honeycomb post-install says the same. - The docs have a search box (press
/), code blocks have a header strip whose Copy button no
longer covers code, and page summaries are whole sentences. Website analytics are off under
Global Privacy Control or Do Not Track, and the footer says what is collected. - Docs: a Concepts glossary, installer options and uninstall steps, every error code with its
exit code, and atccutilreset scoped to spotify-daemon.
spotify-cli v0.1.1
- The daemon no longer hangs while macOS asks whether it may control Spotify. It raises the
Automation dialog itself at startup, and until someone clicks Allow commands fail fast with
automation_permission_pending(exit 4) instead of timing out.spotify doctorno longer
reports the permission as granted after a timeout;spotify daemon statusshows it; the
installer waits for the answer. - Backend: graceful shutdown on SIGTERM;
/healthzreportsservice: spotify-cli.