Audio, playlists and Spotify¶
ApkPy provides the building blocks for music and podcast applications: foreground playback, queues, player bindings, favourites, user playlists, downloads and OAuth.
Start with the copyable Spotify-style background audio guide, then return here for the complete capability matrix and limitations.
What "native playback" means here¶
This is not a player that only works while one screen remains open. When an app
uses background playback, ApkPy generates an Android foreground media service
and a MediaSession. The service owns the active source, queue and metadata;
Activities display and control that state instead of creating unrelated players.
| Area | Available today |
|---|---|
| Sources | Local paths and normal HTTP/HTTPS audio sources accepted by Android's media stack |
| Background | Playback continues across Activity changes, app backgrounding and a locked screen |
| System UI | Notification and lock-screen title, artist, artwork, state and transport controls |
| Focus | Native pause, duck and resume handling when another app needs audio |
| Queue | Metadata-aware next, previous, shuffle, repeat and start by index or source URL |
| Player UI | Bound progress, elapsed/duration, cover, title, artist and synchronized controls |
| Library | Persistent favourites and editable user playlists |
| Offline | Explicit asynchronous downloads to app-private storage |
| Preview | The same public playback, queue, binding and playlist calls for desktop testing |
The generated service also guards calls to duration and current position until the Android player is prepared, reports buffering while preparing, and retries the same source once after a preparation failure. A failed source is not used as a reason to skip rapidly through the rest of the queue.
Precise support boundary
Artwork can use ApkPy's bounded image cache, and complete audio files can be downloaded explicitly. The current player does not yet promise transparent audio caching, adaptive quality selection, guaranteed gapless playback, crossfade, DRM or resumable downloads with progress. ApkPy does not transcode a stream: its bitrate and quality come from the source selected by the app.
Play audio¶
Use background playback when audio should continue while the user changes screen or leaves the app:
audio.play_background(
"https://cdn.example.com/midnight-drive.mp3",
title="Midnight Drive",
artist="Nova",
art="https://cdn.example.com/midnight-drive.jpg",
)
Android uses a foreground media service. The notification and system media controls are maintained by the generated native project.
Queues¶
sources = [
"https://cdn.example.com/track-1.mp3",
"https://cdn.example.com/track-2.mp3",
]
audio.play_playlist(
sources,
titles=["First Light", "Midnight Drive"],
artists=["Nova", "Nova"],
arts=["cover-1.jpg", "cover-2.jpg"],
start=0,
)
audio.next()
audio.previous()
audio.shuffle()
audio.repeat()
The start value can be an index or the selected source URL.
Bind a player screen¶
progress = inputs("", type="range", screen=player)
elapsed = label("0:00 / 0:00", screen=player)
cover = image("", screen=player)
title = label("", screen=player)
artist = label("", screen=player)
play_pause = button("Play", variant="icon", icon="play_arrow", screen=player)
shuffle = button("Shuffle", variant="icon", icon="shuffle", screen=player)
repeat = button("Repeat", variant="icon", icon="repeat", screen=player)
audio.now_playing(
progress=progress,
time=elapsed,
cover=cover,
title=title,
artist=artist,
)
audio.controls(
play_pause=play_pause,
shuffle=shuffle,
repeat=repeat,
)
Moving the bound range seeks through the track. The labels, artwork and buttons stay synchronized with playback.
Favourites¶
The binding watches the current track and refreshes the button/list automatically.
User playlists¶
audio.add_to_playlist("Focus")
audio.play_saved_playlist("Focus")
audio.playlists_list(playlist_list)
audio.edit_playlist("Focus")
audio.playlist_editor(playlist_tracks)
audio.remove_from_playlist("Focus")
audio.delete_playlist("Focus")
When add_to_playlist or remove_from_playlist receives no explicit item, it uses the current track. You may also pass an item dictionary containing src, title, artist and art.
Playlist lists and editors refresh when their screen resumes, keeping the Previewer and generated Android app aligned.
Offline downloads¶
def downloaded(success, path):
if success:
audio.play(path)
else:
toast("Download failed")
files.download(
"https://cdn.example.com/midnight-drive.mp3",
"midnight-drive.mp3",
on_result=downloaded,
)
if files.exists("midnight-drive.mp3"):
audio.play(files.path("midnight-drive.mp3"))
files.delete("midnight-drive.mp3")
Files are stored in app-private storage, so broad storage permission is not required.
Spotify OAuth¶
def signed_in(user):
account_name.set_value(user["name"])
auth.login(
provider="spotify",
client_id="YOUR_SPOTIFY_CLIENT_ID",
scopes=["user-read-email", "user-read-private"],
then=home,
)
auth.user(on_result=signed_in)
token = auth.token()
logged_in = auth.is_logged_in()
auth.logout()
Register both redirect styles with the provider:
- Android:
apkpy://auth - Previewer: the loopback callback shown by the login flow, normally
http://127.0.0.1:8888/callback
OAuth uses Authorization Code with PKCE, avoiding an embedded client secret in the APK.
What ApkPy does not include
ApkPy provides playback, interface, download, playlist and authentication primitives. It does not provide Spotify catalogue rights or bypass provider rules. Only access media and APIs that your application is authorized to use.