Rename youtube-music module to innertube and rewrite it

This commit is contained in:
vfsfitvnm
2022-10-02 15:25:07 +02:00
parent 4bc3671be1
commit 917e194d63
126 changed files with 2210 additions and 2145 deletions

View File

@@ -0,0 +1,59 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class BrowseResponse(
val contents: Contents?,
val header: Header?,
val microformat: Microformat?
) {
@Serializable
data class Contents(
val singleColumnBrowseResultsRenderer: Tabs?,
val sectionListRenderer: SectionListRenderer?,
)
@Serializable
data class Header(
val musicImmersiveHeaderRenderer: MusicImmersiveHeaderRenderer?,
val musicDetailHeaderRenderer: MusicDetailHeaderRenderer?,
) {
@Serializable
data class MusicDetailHeaderRenderer(
val title: Runs?,
val subtitle: Runs?,
val secondSubtitle: Runs?,
val thumbnail: ThumbnailRenderer?,
)
@Serializable
data class MusicImmersiveHeaderRenderer(
val description: Runs?,
val playButton: PlayButton?,
val startRadioButton: StartRadioButton?,
val thumbnail: ThumbnailRenderer?,
val title: Runs?
) {
@Serializable
data class PlayButton(
val buttonRenderer: ButtonRenderer?
)
@Serializable
data class StartRadioButton(
val buttonRenderer: ButtonRenderer?
)
}
}
@Serializable
data class Microformat(
val microformatDataRenderer: MicroformatDataRenderer?
) {
@Serializable
data class MicroformatDataRenderer(
val urlCanonical: String?
)
}
}

View File

@@ -0,0 +1,8 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class ButtonRenderer(
val navigationEndpoint: NavigationEndpoint?
)

View File

@@ -0,0 +1,48 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class Context(
val client: Client,
val thirdParty: ThirdParty? = null,
) {
@Serializable
data class Client(
val clientName: String,
val clientVersion: String,
val visitorData: String?,
val hl: String = "en",
)
@Serializable
data class ThirdParty(
val embedUrl: String,
)
companion object {
val DefaultWeb = Context(
client = Client(
clientName = "WEB_REMIX",
clientVersion = "1.20220328.01.00",
visitorData = "CgtsZG1ySnZiQWtSbyiMjuGSBg%3D%3D"
)
)
val DefaultAndroid = Context(
client = Client(
clientName = "ANDROID",
clientVersion = "16.50",
visitorData = null,
)
)
val DefaultAgeRestrictionBypass = Context(
client = Client(
clientName = "TVHTML5_SIMPLY_EMBEDDED_PLAYER",
clientVersion = "2.0",
visitorData = null,
)
)
}
}

View File

@@ -0,0 +1,17 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class Continuation(
@JsonNames("nextContinuationData", "nextRadioContinuationData")
val nextContinuationData: Data?
) {
@Serializable
data class Data(
val continuation: String?
)
}

View File

@@ -0,0 +1,18 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class ContinuationResponse(
val continuationContents: ContinuationContents?,
) {
@Serializable
data class ContinuationContents(
@JsonNames("musicPlaylistShelfContinuation")
val musicShelfContinuation: MusicShelfRenderer?,
val playlistPanelContinuation: NextResponse.MusicQueueRenderer.Content.PlaylistPanelRenderer?,
)
}

View File

@@ -0,0 +1,13 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class GetQueueResponse(
val queueDatas: List<QueueData>?,
) {
@Serializable
data class QueueData(
val content: NextResponse.MusicQueueRenderer.Content.PlaylistPanelRenderer.Content?
)
}

View File

@@ -0,0 +1,13 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class GridRenderer(
val items: List<Item>?,
) {
@Serializable
data class Item(
val musicTwoRowItemRenderer: MusicTwoRowItemRenderer?
)
}

View File

@@ -0,0 +1,34 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class MusicCarouselShelfRenderer(
val header: Header?,
val contents: List<Content>,
) {
@Serializable
data class Content(
val musicTwoRowItemRenderer: MusicTwoRowItemRenderer?,
val musicResponsiveListItemRenderer: MusicResponsiveListItemRenderer?,
)
@Serializable
data class Header(
val musicTwoRowItemRenderer: MusicTwoRowItemRenderer?,
val musicResponsiveListItemRenderer: MusicResponsiveListItemRenderer?,
val musicCarouselShelfBasicHeaderRenderer: MusicCarouselShelfBasicHeaderRenderer?
) {
@Serializable
data class MusicCarouselShelfBasicHeaderRenderer(
val moreContentButton: MoreContentButton?,
val title: Runs?,
val strapline: Runs?,
) {
@Serializable
data class MoreContentButton(
val buttonRenderer: ButtonRenderer?
)
}
}
}

View File

@@ -0,0 +1,25 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class MusicResponsiveListItemRenderer(
val fixedColumns: List<FlexColumn>?,
val flexColumns: List<FlexColumn>,
val thumbnail: ThumbnailRenderer?,
val navigationEndpoint: NavigationEndpoint?,
) {
@Serializable
data class FlexColumn(
@JsonNames("musicResponsiveListItemFixedColumnRenderer")
val musicResponsiveListItemFlexColumnRenderer: MusicResponsiveListItemFlexColumnRenderer?
) {
@Serializable
data class MusicResponsiveListItemFlexColumnRenderer(
val text: Runs?
)
}
}

View File

@@ -0,0 +1,41 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class MusicShelfRenderer(
val bottomEndpoint: NavigationEndpoint?,
val contents: List<Content>?,
val continuations: List<Continuation>?,
val title: Runs?
) {
@Serializable
data class Content(
val musicResponsiveListItemRenderer: MusicResponsiveListItemRenderer?,
) {
val runs: Pair<List<Runs.Run>, List<List<Runs.Run>>>
get() = (musicResponsiveListItemRenderer
?.flexColumns
?.firstOrNull()
?.musicResponsiveListItemFlexColumnRenderer
?.text
?.runs
?: emptyList()) to
(musicResponsiveListItemRenderer
?.flexColumns
?.lastOrNull()
?.musicResponsiveListItemFlexColumnRenderer
?.text
?.splitBySeparator()
?: emptyList()
)
val thumbnail: Thumbnail?
get() = musicResponsiveListItemRenderer
?.thumbnail
?.musicThumbnailRenderer
?.thumbnail
?.thumbnails
?.firstOrNull()
}
}

View File

@@ -0,0 +1,11 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class MusicTwoRowItemRenderer(
val navigationEndpoint: NavigationEndpoint?,
val thumbnailRenderer: ThumbnailRenderer?,
val title: Runs?,
val subtitle: Runs?,
)

View File

@@ -0,0 +1,203 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
/**
* watchPlaylistEndpoint: params, playlistId
* watchEndpoint: params, playlistId, videoId, index
* browseEndpoint: params, browseId
* searchEndpoint: params, query
*/
//@Serializable
//data class NavigationEndpoint(
// @JsonNames("watchEndpoint", "watchPlaylistEndpoint", "navigationEndpoint", "browseEndpoint", "searchEndpoint")
// val endpoint: Endpoint
//) {
// @Serializable
// data class Endpoint(
// val params: String?,
// val playlistId: String?,
// val videoId: String?,
// val index: Int?,
// val browseId: String?,
// val query: String?,
// val watchEndpointMusicSupportedConfigs: WatchEndpointMusicSupportedConfigs?,
// val browseEndpointContextSupportedConfigs: BrowseEndpointContextSupportedConfigs?,
// ) {
// @Serializable
// data class WatchEndpointMusicSupportedConfigs(
// val watchEndpointMusicConfig: WatchEndpointMusicConfig
// ) {
// @Serializable
// data class WatchEndpointMusicConfig(
// val musicVideoType: String
// )
// }
//
// @Serializable
// data class BrowseEndpointContextSupportedConfigs(
// val browseEndpointContextMusicConfig: BrowseEndpointContextMusicConfig
// ) {
// @Serializable
// data class BrowseEndpointContextMusicConfig(
// val pageType: String
// )
// }
// }
//}
@Serializable
data class NavigationEndpoint(
val watchEndpoint: Endpoint.Watch?,
val watchPlaylistEndpoint: Endpoint.WatchPlaylist?,
val browseEndpoint: Endpoint.Browse?,
val searchEndpoint: Endpoint.Search?,
) {
val endpoint: Endpoint?
get() = watchEndpoint ?: browseEndpoint ?: watchPlaylistEndpoint ?: searchEndpoint
@Serializable
sealed class Endpoint {
@Serializable
data class Watch(
val params: String? = null,
val playlistId: String? = null,
val videoId: String? = null,
val index: Int? = null,
val playlistSetVideoId: String? = null,
val watchEndpointMusicSupportedConfigs: WatchEndpointMusicSupportedConfigs? = null,
) : Endpoint() {
val type: String?
get() = watchEndpointMusicSupportedConfigs
?.watchEndpointMusicConfig
?.musicVideoType
@Serializable
data class WatchEndpointMusicSupportedConfigs(
val watchEndpointMusicConfig: WatchEndpointMusicConfig?
) {
@Serializable
data class WatchEndpointMusicConfig(
val musicVideoType: String?
)
}
}
@Serializable
data class WatchPlaylist(
val params: String?,
val playlistId: String?,
) : Endpoint()
@Serializable
data class Browse(
val params: String? = null,
val browseId: String? = null,
val browseEndpointContextSupportedConfigs: BrowseEndpointContextSupportedConfigs? = null,
) : Endpoint() {
val type: String?
get() = browseEndpointContextSupportedConfigs
?.browseEndpointContextMusicConfig
?.pageType
@Serializable
data class BrowseEndpointContextSupportedConfigs(
val browseEndpointContextMusicConfig: BrowseEndpointContextMusicConfig
) {
@Serializable
data class BrowseEndpointContextMusicConfig(
val pageType: String
)
}
}
@Serializable
data class Search(
val params: String?,
val query: String,
) : Endpoint()
}
}
//@Serializable(with = NavigationEndpoint.Serializer::class)
//sealed class NavigationEndpoint {
// @Serializable
// data class Watch(
// val watchEndpoint: Data
// ) : NavigationEndpoint() {
// @Serializable
// data class Data(
// val params: String?,
// val playlistId: String,
// val videoId: String,
//// val index: Int?
// val watchEndpointMusicSupportedConfigs: WatchEndpointMusicSupportedConfigs,
// )
//
// @Serializable
// data class WatchEndpointMusicSupportedConfigs(
// val watchEndpointMusicConfig: WatchEndpointMusicConfig
// ) {
// @Serializable
// data class WatchEndpointMusicConfig(
// val musicVideoType: String
// )
// }
// }
//
// @Serializable
// data class WatchPlaylist(
// val watchPlaylistEndpoint: Data
// ) : NavigationEndpoint() {
// @Serializable
// data class Data(
// val params: String?,
// val playlistId: String,
// )
// }
//
// @Serializable
// data class Browse(
// val browseEndpoint: Data
// ) : NavigationEndpoint() {
// @Serializable
// data class Data(
// val params: String?,
// val browseId: String,
// val browseEndpointContextSupportedConfigs: BrowseEndpointContextSupportedConfigs,
// )
//
// @Serializable
// data class BrowseEndpointContextSupportedConfigs(
// val browseEndpointContextMusicConfig: BrowseEndpointContextMusicConfig
// ) {
// @Serializable
// data class BrowseEndpointContextMusicConfig(
// val pageType: String
// )
// }
// }
//
// @Serializable
// data class Search(
// val searchEndpoint: Data
// ) : NavigationEndpoint() {
// @Serializable
// data class Data(
// val params: String?,
// val query: String,
// )
// }
//
// object Serializer : JsonContentPolymorphicSerializer<NavigationEndpoint>(NavigationEndpoint::class) {
// override fun selectDeserializer(element: JsonElement) = when {
// "watchEndpoint" in element.jsonObject -> Watch.serializer()
// "watchPlaylistEndpoint" in element.jsonObject -> WatchPlaylist.serializer()
// "browseEndpoint" in element.jsonObject -> Browse.serializer()
// "searchEndpoint" in element.jsonObject -> Search.serializer()
// else -> TODO()
// }
// }
//}

View File

@@ -0,0 +1,87 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class NextResponse(
val contents: Contents?
) {
@Serializable
data class MusicQueueRenderer(
val content: Content?
) {
@Serializable
data class Content(
@JsonNames("playlistPanelContinuation")
val playlistPanelRenderer: PlaylistPanelRenderer?
) {
@Serializable
data class PlaylistPanelRenderer(
val contents: List<Content>?,
val continuations: List<Continuation>?,
) {
@Serializable
data class Content(
val playlistPanelVideoRenderer: PlaylistPanelVideoRenderer?,
val automixPreviewVideoRenderer: AutomixPreviewVideoRenderer?,
) {
@Serializable
data class AutomixPreviewVideoRenderer(
val content: Content?
) {
@Serializable
data class Content(
val automixPlaylistVideoRenderer: AutomixPlaylistVideoRenderer?
) {
@Serializable
data class AutomixPlaylistVideoRenderer(
val navigationEndpoint: NavigationEndpoint?
)
}
}
}
}
}
}
@Serializable
data class Contents(
val singleColumnMusicWatchNextResultsRenderer: SingleColumnMusicWatchNextResultsRenderer?
) {
@Serializable
data class SingleColumnMusicWatchNextResultsRenderer(
val tabbedRenderer: TabbedRenderer?
) {
@Serializable
data class TabbedRenderer(
val watchNextTabbedResultsRenderer: WatchNextTabbedResultsRenderer?
) {
@Serializable
data class WatchNextTabbedResultsRenderer(
val tabs: List<Tab>?
) {
@Serializable
data class Tab(
val tabRenderer: TabRenderer?
) {
@Serializable
data class TabRenderer(
val content: Content?,
val endpoint: NavigationEndpoint?,
val title: String?
) {
@Serializable
data class Content(
val musicQueueRenderer: MusicQueueRenderer?
)
}
}
}
}
}
}
}

View File

@@ -0,0 +1,46 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class PlayerResponse(
val playabilityStatus: PlayabilityStatus?,
val playerConfig: PlayerConfig?,
val streamingData: StreamingData?,
) {
@Serializable
data class PlayabilityStatus(
val status: String?
)
@Serializable
data class PlayerConfig(
val audioConfig: AudioConfig?
) {
@Serializable
data class AudioConfig(
val loudnessDb: Double?,
val perceptualLoudnessDb: Double?
)
}
@Serializable
data class StreamingData(
val adaptiveFormats: List<AdaptiveFormat>?
) {
@Serializable
data class AdaptiveFormat(
val itag: Int,
val mimeType: String,
val bitrate: Long?,
val averageBitrate: Long?,
val contentLength: Long?,
val audioQuality: String?,
val approxDurationMs: Long?,
val lastModified: Long?,
val loudnessDb: Double?,
val audioSampleRate: Int?,
val url: String?,
)
}
}

View File

@@ -0,0 +1,13 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class PlaylistPanelVideoRenderer(
val title: Runs?,
val longBylineText: Runs?,
val shortBylineText: Runs?,
val lengthText: Runs?,
val navigationEndpoint: NavigationEndpoint?,
val thumbnail: ThumbnailRenderer.MusicThumbnailRenderer.Thumbnail?,
)

View File

@@ -0,0 +1,31 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class Runs(
val runs: List<Run> = listOf()
) {
val text: String
get() = runs.joinToString("") { it.text ?: "" }
fun splitBySeparator(): List<List<Run>> {
return runs.flatMapIndexed { index, run ->
when {
index == 0 || index == runs.lastIndex -> listOf(index)
run.text == "" -> listOf(index - 1, index + 1)
else -> emptyList()
}
}.windowed(size = 2, step = 2) { (from, to) -> runs.slice(from..to) }.let {
it.ifEmpty {
listOf(runs)
}
}
}
@Serializable
data class Run(
val text: String?,
val navigationEndpoint: NavigationEndpoint?,
)
}

View File

@@ -0,0 +1,14 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
@Serializable
data class SearchResponse(
val contents: Contents?,
) {
@Serializable
data class Contents(
val tabbedSearchResultsRenderer: Tabs?
)
}

View File

@@ -0,0 +1,28 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class SearchSuggestionsResponse(
val contents: List<Content>?
) {
@Serializable
data class Content(
val searchSuggestionsSectionRenderer: SearchSuggestionsSectionRenderer?
) {
@Serializable
data class SearchSuggestionsSectionRenderer(
val contents: List<Content>?
) {
@Serializable
data class Content(
val searchSuggestionRenderer: SearchSuggestionRenderer?
) {
@Serializable
data class SearchSuggestionRenderer(
val navigationEndpoint: NavigationEndpoint?,
)
}
}
}
}

View File

@@ -0,0 +1,29 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class SectionListRenderer(
val contents: List<Content>?,
val continuations: List<Continuation>?
) {
@Serializable
data class Content(
@JsonNames("musicImmersiveCarouselShelfRenderer")
val musicCarouselShelfRenderer: MusicCarouselShelfRenderer?,
@JsonNames("musicPlaylistShelfRenderer")
val musicShelfRenderer: MusicShelfRenderer?,
val gridRenderer: GridRenderer?,
val musicDescriptionShelfRenderer: MusicDescriptionShelfRenderer?,
) {
@Serializable
data class MusicDescriptionShelfRenderer(
val description: Runs?,
)
}
}

View File

@@ -0,0 +1,25 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class Tabs(
val tabs: List<Tab>?
) {
@Serializable
data class Tab(
val tabRenderer: TabRenderer?
) {
@Serializable
data class TabRenderer(
val content: Content?,
val title: String?,
val tabIdentifier: String?,
) {
@Serializable
data class Content(
val sectionListRenderer: SectionListRenderer?,
)
}
}
}

View File

@@ -0,0 +1,21 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.Serializable
@Serializable
data class Thumbnail(
val url: String,
val height: Int?,
val width: Int?
) {
val isResizable: Boolean
get() = !url.startsWith("https://i.ytimg.com")
fun size(size: Int): String {
return when {
url.startsWith("https://lh3.googleusercontent.com") -> "$url-w$size-h$size"
url.startsWith("https://yt3.ggpht.com") -> "$url-s$size"
else -> url
}
}
}

View File

@@ -0,0 +1,22 @@
package it.vfsfitvnm.youtubemusic.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class ThumbnailRenderer(
@JsonNames("croppedSquareThumbnailRenderer")
val musicThumbnailRenderer: MusicThumbnailRenderer?
) {
@Serializable
data class MusicThumbnailRenderer(
val thumbnail: Thumbnail?
) {
@Serializable
data class Thumbnail(
val thumbnails: List<it.vfsfitvnm.youtubemusic.models.Thumbnail>?
)
}
}

View File

@@ -0,0 +1,11 @@
package it.vfsfitvnm.youtubemusic.models.bodies
import it.vfsfitvnm.youtubemusic.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class BrowseBody(
val context: Context = Context.DefaultWeb,
val browseId: String,
val params: String? = null
)

View File

@@ -0,0 +1,10 @@
package it.vfsfitvnm.youtubemusic.models.bodies
import it.vfsfitvnm.youtubemusic.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class ContinuationBody(
val context: Context = Context.DefaultWeb,
val continuation: String,
)

View File

@@ -0,0 +1,24 @@
package it.vfsfitvnm.youtubemusic.models.bodies
import it.vfsfitvnm.youtubemusic.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class NextBody(
val context: Context = Context.DefaultWeb,
val videoId: String?,
val isAudioOnly: Boolean = true,
val playlistId: String? = null,
val tunerSettingValue: String = "AUTOMIX_SETTING_NORMAL",
val index: Int? = null,
val params: String? = null,
val playlistSetVideoId: String? = null,
val watchEndpointMusicSupportedConfigs: WatchEndpointMusicSupportedConfigs = WatchEndpointMusicSupportedConfigs(
musicVideoType = "MUSIC_VIDEO_TYPE_ATV"
)
) {
@Serializable
data class WatchEndpointMusicSupportedConfigs(
val musicVideoType: String
)
}

View File

@@ -0,0 +1,11 @@
package it.vfsfitvnm.youtubemusic.models.bodies
import it.vfsfitvnm.youtubemusic.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class PlayerBody(
val context: Context = Context.DefaultAndroid,
val videoId: String,
val playlistId: String? = null
)

View File

@@ -0,0 +1,11 @@
package it.vfsfitvnm.youtubemusic.models.bodies
import it.vfsfitvnm.youtubemusic.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class QueueBody(
val context: Context = Context.DefaultWeb,
val videoIds: List<String>? = null,
val playlistId: String? = null,
)

View File

@@ -0,0 +1,11 @@
package it.vfsfitvnm.youtubemusic.models.bodies
import it.vfsfitvnm.youtubemusic.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class SearchBody(
val context: Context = Context.DefaultWeb,
val query: String,
val params: String
)

View File

@@ -0,0 +1,10 @@
package it.vfsfitvnm.youtubemusic.models.bodies
import it.vfsfitvnm.youtubemusic.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class SearchSuggestionsBody(
val context: Context = Context.DefaultWeb,
val input: String
)