Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/faraasaaay/inner/llms.txt

Use this file to discover all available pages before exploring further.

Inner provides YouTube.player(), YouTube.next(), YouTube.queue(), YouTube.lyrics(), YouTube.related(), and YouTube.transcript() for all playback-related operations. All methods are suspend functions returning Result<T>.

Resolving a stream

YouTube.player(videoId, playlistId, client, signatureTimestamp, poToken, setLogin, authState) calls the InnerTube player endpoint and returns Result<PlayerResponse>. The PlayerResponse contains streaming data (streamingData), video details (videoDetails), and playback tracking URLs (playbackTracking).
ParameterTypeDescription
videoIdStringThe YouTube video ID to resolve
playlistIdString?Optional playlist context for the player
clientYouTubeClientThe client variant to use (affects available formats)
signatureTimestampInt?Signature timestamp for cipher decryption (optional)
poTokenString?Explicit proof-of-origin token (optional; falls back to authState)
setLoginBooleanWhether to include the session cookie (default true)
authStatePlaybackAuthStateAuth context; defaults to currentPlaybackAuthState()
import com.tamed.music.innertube.YouTube
import com.tamed.music.innertube.models.YouTubeClient

val playerResponse = YouTube.player(
    videoId = "dQw4w9WgXcQ",
    playlistId = null,
    client = YouTubeClient.IOS
).getOrThrow()

// Get the best audio format
val audioFormat = playerResponse.streamingData?.adaptiveFormats
    ?.filter { it.mimeType.startsWith("audio/") }
    ?.maxByOrNull { it.bitrate ?: 0 }
println("Stream URL: ${audioFormat?.url}")
For iOS and platforms where PoTokens are difficult to generate, use YouTubeClient.IOS or YouTubeClient.IOS_MUSIC for the player() call — these clients typically do not require service integrity tokens.

Queue management

YouTube.next(endpoint, continuation, followAutomixPreview) fetches the play queue for a given WatchEndpoint and returns Result<NextResult>. The NextResult data class contains:
  • title: String? — the queue title (e.g. playlist or radio name)
  • items: List<SongItem> — tracks in the queue
  • currentIndex: Int? — index of the currently selected track
  • lyricsEndpoint: BrowseEndpoint? — endpoint for fetching lyrics (pass to YouTube.lyrics())
  • relatedEndpoint: BrowseEndpoint? — endpoint for related content (pass to YouTube.related())
  • continuation: String? — token for the next page of queue items
  • endpoint: WatchEndpoint — the current or continuation watch endpoint
When followAutomixPreview is true (the default), Inner automatically resolves any automix preview at the end of the queue into a full radio continuation, appending those items to the returned items list.
import com.tamed.music.innertube.models.WatchEndpoint

val endpoint = WatchEndpoint(videoId = "dQw4w9WgXcQ", playlistId = "PLxxxxxx")
val nextResult = YouTube.next(endpoint).getOrThrow()
nextResult.items.forEach { song ->
    println("${song.title} by ${song.artists.firstOrNull()?.name}")
}
To load the next page of queue items, call YouTube.next again with the continuation token:
var queueResult = YouTube.next(endpoint).getOrThrow()
while (queueResult.continuation != null) {
    val nextEndpoint = WatchEndpoint(videoId = queueResult.endpoint.videoId, playlistId = queueResult.endpoint.playlistId)
    queueResult = YouTube.next(nextEndpoint, continuation = queueResult.continuation).getOrThrow()
    queueResult.items.forEach { println(it.title) }
}

Queue lookup

YouTube.queue(videoIds, playlistId) returns Result<List<SongItem>> for fetching track metadata in bulk by video ID list or by playlist ID. At most YouTube.MAX_GET_QUEUE_SIZE (1000) video IDs may be requested at once.
val songs = YouTube.queue(videoIds = listOf("dQw4w9WgXcQ", "9bZkp7q19f0")).getOrThrow()
songs.forEach { song ->
    println("${song.title}${song.artists.joinToString { it.name }}")
}
Alternatively, pass a playlistId to retrieve all tracks associated with a playlist without needing individual video IDs.

Lyrics

YouTube.lyrics(endpoint) takes a BrowseEndpoint obtained from NextResult.lyricsEndpoint and returns Result<String?>. The result is the raw lyrics text, or null if lyrics are unavailable for that track.
val nextResult = YouTube.next(endpoint).getOrThrow()
val lyricsEndpoint = nextResult.lyricsEndpoint
if (lyricsEndpoint != null) {
    val lyrics = YouTube.lyrics(lyricsEndpoint).getOrNull()
    if (lyrics != null) {
        println(lyrics)
    } else {
        println("Lyrics not available")
    }
}
YouTube.related(endpoint) takes a BrowseEndpoint obtained from NextResult.relatedEndpoint and returns Result<RelatedPage>. The RelatedPage data class contains four typed lists:
  • songs: List<SongItem>
  • albums: List<AlbumItem>
  • artists: List<ArtistItem>
  • playlists: List<PlaylistItem>
val nextResult = YouTube.next(endpoint).getOrThrow()
val relatedEndpoint = nextResult.relatedEndpoint
if (relatedEndpoint != null) {
    val related = YouTube.related(relatedEndpoint).getOrThrow()
    println("Related songs: ${related.songs.size}")
    println("Related artists: ${related.artists.size}")
}

Transcript

YouTube.transcript(videoId) returns Result<String> — a plain-text transcript of the video with timestamps. Each line is formatted as [mm:ss.mmm]text. This uses the WEB client (not WEB_REMIX) internally.
val transcript = YouTube.transcript("dQw4w9WgXcQ").getOrThrow()
println(transcript)
// [00:00.000]We're no strangers to love
// [00:04.000]You know the rules and so do I

Registering playback

YouTube.registerPlayback(playlistId, playbackTracking, authState) pings the playback tracking URL from a PlayerResponse to register a play event for analytics. Call this after starting audio playback. The playbackTracking string should be taken from PlayerResponse.playbackTracking?.videostatsPlaybackUrl?.baseUrl (or the equivalent field your app uses). Inner normalises the domain from s.youtube.com to music.youtube.com automatically and appends a random client playback nonce (cpn).
val playerResponse = YouTube.player(videoId = "dQw4w9WgXcQ", client = YouTubeClient.IOS).getOrThrow()
val trackingUrl = playerResponse.playbackTracking?.videostatsPlaybackUrl?.baseUrl
if (trackingUrl != null) {
    YouTube.registerPlayback(
        playlistId = "PLxxxxxx",
        playbackTracking = trackingUrl
    ).getOrNull() // fire-and-forget; ignore errors
}

Media info

YouTube.getMediaInfo(videoId) returns Result<MediaInfo>. The MediaInfo data class contains metadata and engagement stats for a video:
FieldTypeDescription
videoIdStringThe video ID
titleString?Video title
authorString?Channel name
authorIdString?Channel ID
authorThumbnailString?Channel avatar URL
descriptionString?Video description
uploadDateString?Upload date string
subscribersString?Subscriber count text
viewCountInt?View count
likeInt?Like count
dislikeInt?Dislike count
val info = YouTube.getMediaInfo("dQw4w9WgXcQ").getOrThrow()
println("${info.title} by ${info.author}")
println("Views: ${info.viewCount}  Likes: ${info.like}  Dislikes: ${info.dislike}")

Build docs developers (and LLMs) love