Documentation
¶
Overview ¶
Package tiddl provides a Go client for the Tidal music streaming API.
It supports authentication via the device authorization flow (including a convenient WaitForDeviceAuth helper), track and video metadata retrieval, manifest handling for both BTS and DASH streams, and efficient audio downloads via concurrent segment prefetching.
Index ¶
- Variables
- func ParseTrackID(id string) (uint64, error)
- type Artist
- type AudioMode
- type AudioQuality
- type AuthRequest
- type AuthResult
- type BTSManifest
- type Client
- func (c *Client) Close() error
- func (c *Client) DownloadTrackStream(ctx context.Context, stream Stream) (io.ReadCloser, error)
- func (c *Client) GetSession(ctx context.Context) (Session, error)
- func (c *Client) GetTrack(ctx context.Context, trackID uint64) (Track, error)
- func (c *Client) GetTrackStream(ctx context.Context, trackID uint64, quality AudioQuality, immersiveAudio bool) (Stream, error)
- func (c *Client) GetVideo(ctx context.Context, videoID uint64) (Video, error)
- func (c *Client) GetVideoStream(ctx context.Context, videoID uint64, videoQuality VideoQuality) (VideoStream, error)
- func (c *Client) InitiateDeviceAuth(ctx context.Context) (AuthRequest, error)
- func (c *Client) RefreshAuth(ctx context.Context, refreshToken string) (AuthResult, error)
- func (c *Client) RevokeAuth(ctx context.Context) error
- func (c *Client) SetToken(token *oauth2.Token)
- func (c *Client) SetTokenChanged(f TokenChangedFunc)
- func (c *Client) Token() (*oauth2.Token, error)
- func (c *Client) VerifyDeviceAuth(ctx context.Context, deviceCode string) (AuthResult, error)
- func (c *Client) WaitForDeviceAuth(ctx context.Context, req AuthRequest) (AuthResult, error)
- type DASHManifest
- type Manifest
- type MimeType
- type Option
- func WithAuth(token string) Option
- func WithAuthToken(token *oauth2.Token) Option
- func WithBaseURL(baseURL *url.URL) Option
- func WithClient(id, secret string) Option
- func WithContext(ctx context.Context) Option
- func WithCountryCode(countryCode string) Option
- func WithHTTPClient(httpClient *http.Client) Option
- func WithLogger(logger *slog.Logger) Option
- func WithTokenChanged(f TokenChangedFunc) Option
- type Session
- type Stream
- type StreamInfo
- type Tag
- type TokenChangedFunc
- type Track
- type User
- type Video
- type VideoManifest
- type VideoQuality
- type VideoStream
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrTokenExpired indicates the OAuth2 access token has expired. ErrTokenExpired = errors.New("token expired") // ErrNoTokenProvided indicates no OAuth2 token is available. ErrNoTokenProvided = errors.New("no token provided") // ErrNoOpenAuthSession indicates there is no active authentication session. ErrNoOpenAuthSession = errors.New("no open auth session") // ErrResourceNotFound indicates the requested resource does not exist. ErrResourceNotFound = errors.New("resource not found") // ErrCountryCodeNotSet indicates the client's CountryCode field is empty, // which is required for track and video lookups. ErrCountryCodeNotSet = errors.New("country code not set") // ErrDeviceAuthorizationPending is returned (wrapped) by VerifyDeviceAuth // when the user has not yet approved the device authorization request. // Callers should poll again after the interval from AuthRequest. ErrDeviceAuthorizationPending = errors.New("device authorization pending") // ErrDeviceCodeExpired is returned (wrapped) by VerifyDeviceAuth when the // device code has expired or is no longer valid. ErrDeviceCodeExpired = errors.New("device code expired") )
Functions ¶
func ParseTrackID ¶
Example (Numeric) ¶
package main
import (
"fmt"
"github.com/binozo/go-tiddl"
)
func main() {
id, err := tiddl.ParseTrackID("448702530")
if err != nil {
panic(err)
}
fmt.Println(id)
}
Output: 448702530
Example (Url) ¶
package main
import (
"fmt"
"github.com/binozo/go-tiddl"
)
func main() {
id, err := tiddl.ParseTrackID("https://tidal.com/track/448702530/u")
if err != nil {
panic(err)
}
fmt.Println(id)
}
Output: 448702530
Types ¶
type Artist ¶
type Artist struct {
ID int `json:"id"`
Name string `json:"name"`
Handle *string `json:"handle"`
Type string `json:"type"`
Picture string `json:"picture"`
}
Artist represents a Tidal artist with their basic profile information.
type AudioMode ¶
type AudioMode string
AudioMode represents the audio channel configuration of a stream.
type AudioQuality ¶
type AudioQuality string
AudioQuality represents the audio quality level of a stream.
const ( // Low is a low-bitrate stream. Low AudioQuality = "LOW" // High is a high-bitrate AAC stream. High AudioQuality = "HIGH" // Lossless is a CD-quality FLAC stream (16-bit/44.1 kHz). Lossless AudioQuality = "LOSSLESS" // HiResLossless is a high-resolution FLAC stream (up to 24-bit/192 kHz). HiResLossless AudioQuality = "HI_RES_LOSSLESS" )
type AuthRequest ¶
type AuthRequest struct {
DeviceCode string
Expires time.Time
Interval time.Duration
UserCode string
VerificationUri *url.URL
VerificationUriComplete *url.URL
}
AuthRequest represents a pending device authorization request returned by InitiateDeviceAuth. The user must visit VerificationUriComplete to approve the request before it expires.
func (*AuthRequest) UnmarshalJSON ¶
func (a *AuthRequest) UnmarshalJSON(data []byte) error
type AuthResult ¶
AuthResult represents the result of a successful device authorization, containing the OAuth2 token and user information.
func (*AuthResult) UnmarshalJSON ¶
func (a *AuthResult) UnmarshalJSON(data []byte) error
type BTSManifest ¶
type BTSManifest struct {
MimeType string `json:"mimeType"`
Codecs string `json:"codecs"`
EncryptionType string `json:"encryptionType"`
URLs []string `json:"urls"`
}
BTSManifest represents a BTS-format stream manifest containing direct URLs.
func (BTSManifest) GetCodecs ¶
func (b BTSManifest) GetCodecs() string
GetCodecs returns the codec identifier for the stream.
func (BTSManifest) GetURLs ¶
func (b BTSManifest) GetURLs() []string
GetURLs returns the download URLs for the stream segments.
type Client ¶
type Client struct {
CountryCode string
// contains filtered or unexported fields
}
Client is a Tidal API client. It handles authentication, request building, and token lifecycle management.
func NewClient ¶
NewClient creates a new Tidal API client with the given options. By default, it uses Tidal's public client credentials and the v1 API base URL.
Use WithClient to provide custom OAuth2 credentials, WithAuth or WithAuthToken to provide an existing token, and WithCountryCode to set the country code required for track and video lookups.
func (*Client) DownloadTrackStream ¶
DownloadTrackStream returns an io.ReadCloser that streams the audio data from the given Stream's manifest URLs. The reader prefetches segments concurrently for improved throughput. The caller must close the reader when done.
func (*Client) GetSession ¶
GetSession retrieves the current session information from the Tidal API. The returned Session contains the user's country code, which can be used to populate Client.CountryCode for subsequent track and video lookups.
func (*Client) GetTrack ¶
GetTrack retrieves metadata for a track by its ID. Requires Client.CountryCode to be set.
func (*Client) GetTrackStream ¶
func (c *Client) GetTrackStream(ctx context.Context, trackID uint64, quality AudioQuality, immersiveAudio bool) (Stream, error)
GetTrackStream retrieves playback information for a track, including the stream manifest needed to download the audio data. The immersiveAudio parameter enables Dolby Atmos when set to true.
func (*Client) GetVideo ¶
GetVideo retrieves metadata for a video by its ID. Requires Client.CountryCode to be set.
func (*Client) GetVideoStream ¶
func (c *Client) GetVideoStream(ctx context.Context, videoID uint64, videoQuality VideoQuality) (VideoStream, error)
GetVideoStream retrieves playback information for a video, including the stream manifest needed to download the video data.
func (*Client) InitiateDeviceAuth ¶
func (c *Client) InitiateDeviceAuth(ctx context.Context) (AuthRequest, error)
InitiateDeviceAuth starts the device authorization flow. The returned AuthRequest contains a URL the user must visit to approve the request and a device code used to poll for completion via VerifyDeviceAuth.
func (*Client) RefreshAuth ¶
RefreshAuth refreshes the authentication using the provided refresh token. On success, the client's token source is automatically updated.
func (*Client) RevokeAuth ¶
RevokeAuth logs out the current session by calling the Tidal logout endpoint.
func (*Client) SetToken ¶
SetToken updates the client's OAuth2 token and reconfigures the HTTP client to use the new token with automatic refresh. Tokens are reused with a 5-minute early expiry margin to proactively refresh before expiration.
func (*Client) SetTokenChanged ¶
func (c *Client) SetTokenChanged(f TokenChangedFunc)
SetTokenChanged registers (or replaces) a callback that will be invoked when the client detects a new token has been obtained.
It is safe to call at any time. The callback will be used for all subsequently created token sources (via SetToken, VerifyDeviceAuth, RefreshAuth, etc.). Callbacks registered this way are best-effort for already-active sources.
func (*Client) Token ¶
Token returns the current OAuth2 token from the active token source. Returns ErrNoTokenProvided if no token source is configured.
func (*Client) VerifyDeviceAuth ¶
VerifyDeviceAuth completes the device authorization flow by exchanging the device code for access and refresh tokens. On success, the client's token source is automatically updated.
func (*Client) WaitForDeviceAuth ¶
func (c *Client) WaitForDeviceAuth(ctx context.Context, req AuthRequest) (AuthResult, error)
WaitForDeviceAuth repeatedly calls VerifyDeviceAuth (respecting the Interval from the AuthRequest) until the user approves the request on the Tidal side, the code expires, or the context is cancelled.
This is the recommended helper for completing a device flow started by InitiateDeviceAuth.
type DASHManifest ¶
DASHManifest represents a DASH-format stream manifest with resolved segment URLs.
func (DASHManifest) GetCodecs ¶
func (d DASHManifest) GetCodecs() string
GetCodecs returns the codec identifier for the stream.
func (DASHManifest) GetURLs ¶
func (d DASHManifest) GetURLs() []string
GetURLs returns the download URLs for the stream segments.
type Manifest ¶
Manifest provides a common interface for accessing stream URLs and codec information from different manifest types.
type Option ¶
type Option func(*config)
Option configures a Client via functional options passed to NewClient.
func WithAuth ¶
WithAuth configures the client with a static access token that cannot be refreshed. For tokens that support refresh, use WithAuthToken instead.
func WithAuthToken ¶
WithAuthToken configures the client with an OAuth2 token that supports automatic refresh. The token is reused with a 5-minute early expiry margin.
If a TokenChangedFunc has been registered (via WithTokenChanged), the change detector is placed between the raw refreshing source and the outer reuse (sandwich style) so that refreshed tokens are observed.
func WithBaseURL ¶
WithBaseURL overrides the Tidal API base URL. Useful for testing or alternative endpoints.
func WithClient ¶
WithClient sets custom OAuth2 client ID and secret instead of the default Tidal public credentials.
func WithContext ¶
WithContext sets the parent context for the client. The client derives a cancellable context from it, which is released on Close.
func WithCountryCode ¶
WithCountryCode sets the ISO 3166-1 alpha-2 country code required for track and video API calls (e.g. "US", "DE").
func WithHTTPClient ¶
WithHTTPClient sets a custom HTTP client. By default, the client creates one with OAuth2 transport wrapping http.DefaultTransport (no overall timeout, rely on context cancellation for requests).
func WithLogger ¶
WithLogger sets the structured logger used by the client for debug output.
func WithTokenChanged ¶
func WithTokenChanged(f TokenChangedFunc) Option
WithTokenChanged registers a callback that will be invoked when the client obtains a new token (typically after an automatic refresh, including refresh token rotation).
The callback is used for all token sources created after this option is applied (construction-time sources, SetToken, VerifyDeviceAuth, etc.). It is the recommended way to persist tokens so that rotated refresh tokens are not lost when the process exits.
type Session ¶
type Session struct {
SessionID string `json:"sessionId"`
UserID int `json:"userId"`
CountryCode string `json:"countryCode"`
ChannelID int `json:"channelId"`
PartnerID int `json:"partnerId"`
Client struct {
ID int `json:"id"`
Name string `json:"name"`
AuthorizedForOffline bool `json:"authorizedForOffline"`
AuthorizedForOfflineDate *string `json:"authorizedForOfflineDate"`
} `json:"client"`
}
Session represents an active Tidal API session.
type Stream ¶
type Stream struct {
TrackID int `json:"trackId"`
AssetPresentation string `json:"assetPresentation"`
AudioMode AudioMode `json:"audioMode"`
AudioQuality AudioQuality `json:"audioQuality"`
ManifestMimeType MimeType `json:"manifestMimeType"`
ManifestHash string `json:"manifestHash"`
Manifest Manifest
Info *StreamInfo
}
Stream represents a playable Tidal audio stream, including the manifest needed to download the actual audio data.
func (*Stream) UnmarshalJSON ¶
type StreamInfo ¶
type StreamInfo struct {
AlbumReplayGain float64 `json:"albumReplayGain"`
AlbumPeakAmplitude float64 `json:"albumPeakAmplitude"`
TrackReplayGain float64 `json:"trackReplayGain"`
TrackPeakAmplitude float64 `json:"trackPeakAmplitude"`
BitDepth int `json:"bitDepth"`
SampleRate int `json:"sampleRate"`
}
StreamInfo contains audio quality metadata such as replay gain, bit depth, and sample rate.
type Tag ¶
type Tag string
Tag represents a quality tag attached to a track's media metadata.
func (Tag) Quality ¶
func (t Tag) Quality() AudioQuality
Quality maps the tag to its corresponding AudioQuality value.
type TokenChangedFunc ¶
TokenChangedFunc is called whenever the client obtains a new token, typically because an automatic refresh occurred (including cases where the authorization server rotates the refresh token).
The token passed to the function is a shallow copy. Callers must not mutate it. The callback is invoked synchronously from the token acquisition path (i.e. during HTTP requests that require authorization or explicit calls to Token()) and should return quickly.
type Track ¶
type Track struct {
ID int `json:"id"`
Title string `json:"title"`
Duration int `json:"duration"`
ReplayGain float64 `json:"replayGain"`
Peak float64 `json:"peak"`
AllowStreaming bool `json:"allowStreaming"`
StreamReady bool `json:"streamReady"`
PayToStream bool `json:"payToStream"`
AdSupportedStreamReady bool `json:"adSupportedStreamReady"`
DjReady bool `json:"djReady"`
StemReady bool `json:"stemReady"`
StreamStartDate string `json:"streamStartDate"`
PremiumStreamingOnly bool `json:"premiumStreamingOnly"`
TrackNumber int `json:"trackNumber"`
VolumeNumber int `json:"volumeNumber"`
Version *string `json:"version"`
Popularity int `json:"popularity"`
Copyright string `json:"copyright"`
Bpm int `json:"bpm"`
Key string `json:"key"`
KeyScale string `json:"keyScale"`
URL string `json:"url"`
Isrc string `json:"isrc"`
Editable bool `json:"editable"`
Explicit bool `json:"explicit"`
AudioQuality string `json:"audioQuality"`
AudioModes []string `json:"audioModes"`
MediaMetadata struct {
Tags []Tag `json:"tags"`
} `json:"mediaMetadata"`
Upload bool `json:"upload"`
AccessType string `json:"accessType"`
Spotlighted bool `json:"spotlighted"`
Ai bool `json:"ai"`
Artist Artist `json:"artist"`
Artists []Artist `json:"artists"`
Album struct {
ID int `json:"id"`
Title string `json:"title"`
Cover string `json:"cover"`
VibrantColor *string `json:"vibrantColor"`
VideoCover any `json:"videoCover"`
} `json:"album"`
Mixes struct {
TRACKMIX string `json:"TRACK_MIX"`
} `json:"mixes"`
}
Track represents a Tidal track with its metadata.
func (*Track) BestQuality ¶
func (t *Track) BestQuality() AudioQuality
BestQuality returns the highest available AudioQuality based on the track's media metadata tags. If no tags are present, it defaults to Low.
type User ¶
type User struct {
UserID int `json:"userId"`
Username string `json:"username"`
AcceptedEULA bool `json:"acceptedEULA"`
AccountLinkCreated bool `json:"accountLinkCreated"`
Address *string `json:"address"`
AppleUID *string `json:"appleUid"`
Birthday time.Time `json:"birthday"`
ChannelID int `json:"channelId"`
City *string `json:"city"`
CountryCode string `json:"countryCode"`
Created time.Time `json:"created"`
Email string `json:"email"`
EmailVerified bool `json:"emailVerified"`
FirstName *string `json:"firstName"`
FullName *string `json:"fullName"`
GoogleUID *string `json:"googleUid"`
LastName *string `json:"lastName"`
NewUser bool `json:"newUser"`
Nickname *string `json:"nickname"`
ParentID int `json:"parentId"`
PhoneNumber *string `json:"phoneNumber"`
PostalCode *string `json:"postalcode"`
Updated time.Time `json:"updated"`
USState *string `json:"usState"`
}
User represents a Tidal user account.
func (*User) UnmarshalJSON ¶
type Video ¶
type Video struct {
ID int `json:"id"`
Title string `json:"title"`
VolumeNumber int `json:"volumeNumber"`
TrackNumber int `json:"trackNumber"`
ReleaseDate time.Time `json:"releaseDate"`
ImagePath *string `json:"imagePath"`
ImageID string `json:"imageId"`
VibrantColor string `json:"vibrantColor"`
Duration time.Duration `json:"duration"`
Quality VideoQuality `json:"quality"`
StreamReady bool `json:"streamReady"`
AdSupportedStreamReady bool `json:"adSupportedStreamReady"`
DjReady bool `json:"djReady"`
StemReady bool `json:"stemReady"`
StreamStartDate time.Time `json:"streamStartDate"`
AllowStreaming bool `json:"allowStreaming"`
Explicit bool `json:"explicit"`
Popularity int `json:"popularity"`
Type string `json:"type"`
AdsURL *string `json:"adsUrl"`
AdsPrePaywallOnly bool `json:"adsPrePaywallOnly"`
Artist Artist `json:"artist"`
Artists []Artist `json:"artists"`
Album *string `json:"album"`
}
Video represents a Tidal video with its metadata.
func (*Video) UnmarshalJSON ¶
type VideoManifest ¶
VideoManifest holds the decoded video stream manifest with download URLs.
type VideoQuality ¶
type VideoQuality string
VideoQuality represents the quality level of a video stream.
const ( // VideoAudioOnly fetches only the audio track of a video. VideoAudioOnly VideoQuality = "AUDIO_ONLY" // VideoLow is a low-resolution video stream. VideoLow VideoQuality = "LOW" // VideoMedium is a medium-resolution video stream. VideoMedium VideoQuality = "MEDIUM" // VideoHigh is a high-resolution video stream. VideoHigh VideoQuality = "HIGH" )
type VideoStream ¶
type VideoStream struct {
VideoID int `json:"videoId"`
StreamType string `json:"streamType"`
AssetPresentation string `json:"assetPresentation"`
VideoQuality string `json:"videoQuality"`
ManifestMimeType string `json:"manifestMimeType"`
ManifestHash string `json:"manifestHash"`
Manifest VideoManifest `json:"manifest"`
}
VideoStream represents a playable Tidal video stream with its manifest.
func (*VideoStream) UnmarshalJSON ¶
func (vs *VideoStream) UnmarshalJSON(data []byte) error