rocksky — Python SDK
Python bindings to the shared Rocksky Rust core (rocksky-sdk) via
UniFFI: AppView reads, AT Protocol PDS
writes (scrobble fan-out, like, follow, shout, now-playing), a local
duplicate-prevention index, and the identity hashes — the same engine behind
every Rocksky SDK (Ruby, Kotlin, Clojure, Erlang/Elixir/Gleam).
Install
pip install rocksky==0.7.0 # or: uv add rocksky==0.7.0
The wheel is pure-Python; the ~14 MB native library is fetched from the GitHub release on first import and cached (checksum-verified). For a local checkout, build it once:
./build-core.sh
Quickstart
from rocksky import AppView, Agent, ScrobbleInput, song_hash
# Reads — unauthenticated. Pass a base URL to override https://api.rocksky.app.
av = AppView() # defaults to https://api.rocksky.app; AppView(base, token) for auth-gated reads
print(av.global_stats().scrobbles)
for t in av.top_tracks(10, 0):
print(t.artist, "—", t.title)
# Typed date-window charts, and a bare title+artist resolved to canonical metadata.
from rocksky import DateInterval
for t in av.top_tracks_interval(10, 0, DateInterval.LAST_DAYS(days=7)):
print(t.artist, "—", t.title)
meta = av.match_song("Chaser", "Calibro 35", None, None) # JSON string
# Writes — log in once (session persisted at the given path).
agent = Agent.login_password("session.json", "alice.bsky.social", "app-password", None, None)
out = agent.scrobble(ScrobbleInput(
title="Chaser", artist="Calibro 35",
album="Jazzploitation", album_artist="Calibro 35", duration_ms=182320,
))
print(out.scrobble_uri) # also artist_uri / album_uri / song_uri
API
Reads — AppView(base=None, token=None)
Pass a base URL to target a custom AppView; pass token to send
Authorization: Bearer <token> on every read (for auth-gated queries).
Typed views (return dataclasses): profile(actor),
scrobbles(actor, limit, offset), songs(actor, limit, offset),
albums(actor, limit, offset), artists(actor, limit, offset),
loved_songs(actor, limit, offset), catalog_albums, catalog_artists,
catalog_songs, album_tracks(uri), artist_albums(uri), artist_tracks,
scrobble_feed, scrobble(uri) (single), follows, followers,
known_followers, top_tracks(limit, offset), top_artists(limit, offset),
global_stats().
Typed date-window charts: top_tracks_interval(limit, offset, interval) and
top_artists_interval(...), where interval is a DateInterval:
DateInterval.ALL_TIME, DateInterval.LAST_DAYS(days=7),
DateInterval.LAST_WEEKS(weeks=n), DateInterval.LAST_MONTHS(months=n),
DateInterval.LAST_YEARS(years=n), DateInterval.RANGE(start=..., end=...)
(RFC-3339 bounds). top_tracks / top_artists are the all-time shorthands.
Match: match_song(title, artist, mb_id, isrc) resolves a bare title+artist
into full canonical metadata (returns a JSON string).
Raw-JSON long tail (each returns a JSON string): album, artist, song,
playlists, playlist, stats, wrapped, scrobbles_chart,
recommendations, neighbours, shouts, and more.
Universal escape hatch: av.get(nsid, params) calls any read query by
NSID — params is a dict[str, str], and it returns a JSON string.
Writes — Agent
Agent.login_password(session_path, identifier, password, appview=None, dedup_path=None)
returns an agent. Then:
scrobble(ScrobbleInput)→ScrobbleResult— from full metadata, writes artist + album + song + scrobble, skipping any that already exist (with a dedup store).scrobble_match(ScrobbleMatchInput)→ScrobbleResult— resolves the track viamatch_songfirst, then fans out the same way.ScrobbleMatchInput(title, artist, album=None, mb_id=None, isrc=None, timestamp=None)—title/artistrequired;albumoverrides the resolved album,mb_id/isrcare match anchors,timestampis the scrobbled-at Unix seconds (e.g.agent.scrobble_match(ScrobbleMatchInput(title="Chaser", artist="Calibro 35"))).create_song(SongInput),create_album(AlbumInput),create_artist(ArtistInput)like(uri, cid),unlike(uri),follow(did),unfollow(did)shout(subject_uri, subject_cid, message),reply_shout(...)set_now_playing(NowPlayingInput),clear_now_playing()refresh_session(),did(),profile()sync_repo()— mirror the repo into the local dedup index, returning per-collection counts as JSON (needsdedup_path)hydrate_from_jetstream()— keep the dedup index live off the Jetstream firehose (needsdedup_path)
Identity hashes
song_hash(title, artist, album), album_hash(album, album_artist),
artist_hash(album_artist) — lowercase-hex SHA-256, identical to the server and
every other Rocksky SDK.
Example
python examples/native_core.py
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rocksky-0.8.0.tar.gz.
File metadata
- Download URL: rocksky-0.8.0.tar.gz
- Upload date:
- Size: 75.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed274d2ac5c88c3f564ec79b2a24a3559e6d2264c4b8d0d2d5e716b06b7e1175
|
|
| MD5 |
cdafcac2f06fc5e8582a056f99cc543a
|
|
| BLAKE2b-256 |
231313ccf6263bf3c5edaf7e3341d69d71cc543a6646d8f8200e7255e0e54d92
|
File details
Details for the file rocksky-0.8.0-py3-none-any.whl.
File metadata
- Download URL: rocksky-0.8.0-py3-none-any.whl
- Upload date:
- Size: 35.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ef72c7e553ac0a5a49077c58fced01fd0cede6b9f308d35dfeaca47c39f64e7
|
|
| MD5 |
daffa28080b08cd962bdd8af3f859859
|
|
| BLAKE2b-256 |
289655333e90547f6d31cdd1b2bafa891d440b899c12b1e4333708928e351894
|