Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

only 100 songs from a playlist show up #841

Open
3 tasks done
Chispaff opened this issue Feb 1, 2025 · 3 comments
Open
3 tasks done

only 100 songs from a playlist show up #841

Chispaff opened this issue Feb 1, 2025 · 3 comments
Labels
bug Something isn't working

Comments

@Chispaff
Copy link

Chispaff commented Feb 1, 2025

Steps to reproduce the bug

Try to sync a youtube playlist with ViTune that has around 4000 songs on it.

Expected behavior

The songs should sync

Actual behavior

I have a playlist with around 4000 songs on it. However, there are only 100 songs showing up and it refuses to sync anymore. This was not happening before and it would sync all the songs. When it wouldnt, it would sync over 500 of them. This is really frustrating and I'm not sure what the issue is.

Screenshots/Screen recordings

No response

Logs

No response

ViTune version

v1.1.3

What kind of build are you using?

Release (GitHub / F-Droid)

Android version

Android 14

Device info

I am using a Samsung Galaxy A35, Android 14

Upstream reproducibility

No response

Additional information

No response

Checklist

  • I am able to reach YouTube Music through the official app/website and can confirm that I live in a supported country (and this country is not temporarily unavailable)
  • I am not using a proxy, VPN, device policy or anything else that restricts access to YouTube Music
  • This issue has not been submitted already (this is not a duplicate of another issue)
@Chispaff Chispaff added the bug Something isn't working label Feb 1, 2025
@th3y
Copy link

th3y commented Feb 1, 2025

Yes, broken today in unnofficial apps, seems like continuations key (The one that works to show all the songs in a playlists or album) its still there, but its not where it should be.

Update: can't find the right continuation.

Seems like the key changed, and now its something like this (But im not sure)
"continuationItemRenderer": {
"trigger": "CONTINUATION_TRIGGER_ON_ITEM_PRESCAN_VISIBLE",
"continuationEndpoint": {
"clickTrackingParams": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=",
"continuationCommand": {
"token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"request": "CONTINUATION_REQUEST_TYPE_BROWSE"
}
}
}

@th3y
Copy link

th3y commented Feb 5, 2025

Got the fix:

New data class for Continuation

@Serializable
data class ContinuationItemRenderer(
    val trigger: String?,
    val continuationEndpoint: ContinuationEndpoint?
) {
    @Serializable
    data class ContinuationEndpoint(
        val clickTrackingParams: String?,
        val continuationCommand: ContinuationCommand?
    ) {
        @Serializable
        data class ContinuationCommand(
            val token: String?,
            val request: String?
        )
    }
}

New File: ContinuationResponse.kt

@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class ContinuationResponse(
    val onResponseReceivedActions: List<ResponseAction>?,
    val continuationContents: ContinuationContents?
) {

    @Serializable
    data class ResponseAction(
        val appendContinuationItemsAction: ContinuationItems?,
        val clickTrackingParams: String
    )

    @Serializable
    data class ContinuationItems(
        val continuationItems: List<MusicShelfRenderer.Content>?,
    )

    @Serializable
    data class ContinuationContents(
        @JsonNames("musicPlaylistShelfContinuation")
        val musicShelfContinuation: MusicShelfRenderer?,
        val playlistPanelContinuation: NextResponse.MusicQueueRenderer.Content.PlaylistPanelRenderer?
    )
}

New MusicShelfRenderer

@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 continuationItemRenderer: ContinuationItemRenderer? = null
    ) {
        val runs: Pair<List<Runs.Run>, List<List<Runs.Run>>>
            get() = musicResponsiveListItemRenderer
                ?.flexColumns
                ?.firstOrNull()
                ?.musicResponsiveListItemFlexColumnRenderer
                ?.text
                ?.runs
                .orEmpty() to
                    musicResponsiveListItemRenderer
                        ?.flexColumns
                        ?.let { it.getOrNull(1) ?: it.lastOrNull() }
                        ?.musicResponsiveListItemFlexColumnRenderer
                        ?.text
                        ?.splitBySeparator()
                        .orEmpty()

        val thumbnail: Thumbnail?
            get() = musicResponsiveListItemRenderer
                ?.thumbnail
                ?.musicThumbnailRenderer
                ?.thumbnail
                ?.thumbnails
                ?.firstOrNull()

        val continuationToken: String?
            get() = continuationItemRenderer
                ?.continuationEndpoint
                ?.continuationCommand
                ?.token
    }
}

suspend fun Innertube.playlistPage(body: BrowseBody)

//in playlistPage 
val continuationToken = contents
            ?.firstOrNull()
            ?.musicShelfRenderer
            ?.contents
            ?.firstOrNull { it.continuationItemRenderer != null }
            ?.continuationToken

//from Innertube.PlaylistOrAlbumPage(
songsPage =  Innertube.ItemsPage( items =  modifiedMusicShelfRenderer
                ?.toSongsPage()?.items, continuation =continuationToken) ,

suspend fun Innertube.playlistPage(body: ContinuationBody)

suspend fun Innertube.playlistPage(body: ContinuationBody) = runCatchingCancellable {

    val response = client.post(BROWSE) {
        setBody(body)
        body.context.apply()
    }.body<ContinuationResponse>()

    val items = response
        .onResponseReceivedActions
        ?.firstOrNull()
        ?.appendContinuationItemsAction
        ?.continuationItems
        ?.mapNotNull(MusicShelfRenderer.Content::musicResponsiveListItemRenderer)
        ?.mapNotNull(Innertube.SongItem::from)

    val continuation = response
        .onResponseReceivedActions
        ?.firstOrNull()
        ?.appendContinuationItemsAction
        ?.continuationItems
        ?.firstOrNull { it.continuationItemRenderer != null }
        ?.continuationToken

    Innertube.ItemsPage(items = items, continuation = continuation)

My code is rusty but works (Im not going do a pull request)
Edit: This also works for Albums btw.

@CallMeAl3x
Copy link

Can you make a noob insctructions please ? @th3y

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants