-
Notifications
You must be signed in to change notification settings - Fork 325
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
Support for playlists #656
Comments
If they are supported by the mobile client (Android, in particular) then
it should be possible, yes. I’ll have a look, but no promises on my end.
If you want to have some fun, feel free to reverse-engineer it yourself.
|
Update on this issue: Playlists are supported my the mobile API (as expected). The following API calls are probobly relevant: playlists.* The issue I see here is that playlists won’t be useful without proper management interface and catalog browsing. And right now I don’t want to invest that much time into pianobar. So, if anyone want to do this: Go ahead, submit a pull request and we’ll work something out. |
Would you consider simply subscribing to playlists (by URI or ID), rather than full playlist management, to be in-scope? It's something I've been wanting as well, but don't think I care enough about playlist management/curation to implement more than that. |
Sure, if you’re submitting a pull request with “only” basic playback,
I’ll merge that too. Let me know if you need any help.
|
Thanks! As long as you don't mind me taking about a month before I really have time to work on this, you can go ahead and assign it to me. |
I don’t and GitHub does not allow me to assing this issue to you,
unfortunately. But it’s all yours.
|
Perfect. I'll just task this in my trello then! |
Excited to see this feature. |
@sehrgut What ever happened with this? |
I stopped using Pandora. |
I'm interesting into pushing this forward at least to the point of being able play a manually entered playlist. I've snipped the connection between my Android APP and Pandor's cloud while playing a playlist but all of the API comms are over a TLSV1.2 connection. Any hints on how to get the plaintext? Do I need to play DNS games and run an TLS proxy? |
I can’t tell you how to analyze the communication via sniffing. Looking at the APK is usually easier. |
I've make a lot of progress in sniffing the API communications (thank you wireshark, chrome & SSLKEYLOGFILE!). I think I understand a lot of what I need to know. However I now that I've read https://6xq.net/pandora-apidoc/ I understand that there are two APIs and all I've been sniffing the REST API not the JSON API V5 that pianobar uses. I also read that
Does that mean that the APK you used is of an old app that's no longer distributed ? What is the status of PromyLOPh/pandora-apidoc#45 in 2024 ? Is it hopeless? It seems like all new Pandora related development stopped 3 years ago? |
The APKs I have are quite old, so it’s very likely they are not officially distributed any more. It looks like apkmirror.com has older versions though. I don’t know the status of that ticket, because pianobar does not use the REST API and therefore does not have that problem. |
@PromyLOPh Can you tell me what version of the app you based your research on? |
Many, many versions from for example 1.5.14 to 6.3 to 8.5 to 1804.2 to 2007.1. Their versioning does not seem to be very stable over the years, so I can’t tell you which one is “newer”. In the versions I have |
Thanks! apkmirror doesn't have 2007.1, so I'll tied 1812.2. When I decompiled it with jadx I didn't find a ./com/pandora/radio/api/PublicApi.java, but I did find ./com/pandora/radio/api/x.java which I think is it (let's hear it for error logging!). Onward... |
I have the latest build off Playstore (version 2409.1) that I can share. Reach out via my contact information on my profile page. You can also pull the APK directly off of any android device with developer mode enabled (does not require root) using ADB. Use |
@minecraftchest1 Thanks, but I'm good with 1812.2. I was looking for an older version that was as close to what PromyLOPh used as possible. From what I've read later versions switched to using the RESP API rather than the JSON V5 API that pianobar uses. Reverse engineering the RESP API is much easier as javascript source code with comments are easily pulled from Pandora's web player. If panobar used the RESP API I'd probably be done already. |
@PromyLOPh I've made some good progress reverse engineering the APIs I need to add support for playlists. While I was there I also reverse engineered the podcast and album APIs. My plan is to add a new mode concept with choices of station, playlist, podcast or albums. Free users will have access to stations and podcasts. Subscription users with a will have access to playlists and premium subscription uses will have access to albums. On startup pianobar would behave identically to the current code. If the user selects a new mode pianobar would switch to the selected mode and play the selected station, podcast, playlist or album. Podcasts, playlists and albums have a finite length, once they have played to completion pianobar would return to station mode. The default keyboard shortcut will be 'm'. Does the approach sound reasonable? |
What does playlist/podcast/album selection look like? I’m assuming playlists are owned by a user just like stations, so you’ll just list them; podcasts and albums probably need a search? |
A list of podcasts, playlists or albums is displayed from the user's "collection". Playlists are created by users and automatically by Pandora based on what Pandor thinks the user likes. I'm NOT currently planning on adding support for "collecting", just support for playing items collected by the user using a standard Pandor App. Searching for albums and podcasts and "collecting" could certainly be added. So exactly the same as selecting a station, expect with a list of playlists, or podcasts or albums. |
I see. Do you think it would be feasible to display all of those in a single list (the current “station list”) and using filters to narrow things down? Or would that be too messy? |
Sure, that could work. I could add a "all" mode that would display everything. |
FYI: This is what my kludged up "station" list looks like currently:
And this is what the it looks like in playlist mode:
P = playlist |
Sorry if I'm too late on this info, I've been busy with exams for the last few weeks. Here's what I know about the JSON playlist API. I managed to play a playlist in my app a couple of years ago, but haven't really touched it since - though I don't think much has changed, looking at the latest official app. Note If I may toot my own horn for a second, I really do recommend my tool Pandora MITM for reverse-engineering the JSON API. I've been working on it in private for the past couple of years (I'm ripping out the base and turning it into a generic programmable MITM platform), but the current public version still works fine. The API definitions detailed below all come straight from the inference plugin, which can generate Markdown among other things. These are approximations with a fairly low sample size, though. There are two important API methods when it comes to playing a playlist:
|
Field | Type | Optional | Default |
---|---|---|---|
request | JSON object | No |
request
Object
Field | Type | Optional | Default |
---|---|---|---|
limit | Integer | No | |
annotationLimit | Integer | No | |
pandoraId | String<Pandora ID> | No | |
bypassPrivacyRules | Boolean | No | |
playlistVersion | Integer | Yes |
Response
playlists.v7.getTracks
Object
Field | Type | Optional | Default |
---|---|---|---|
notModified | Boolean | No | |
tracks | List<JSON object> | Yes | |
offset | Integer | Yes | |
annotations | Map<String<Pandora ID>, Annotation> | No | |
pandoraId | String<Pandora ID> | No | |
version | Integer | No | |
name | String | No | |
description | String | No | |
timeCreated | Timestamp<Epoch<Milliseconds>> | No | |
isPrivate | Boolean | No | |
secret | Boolean | No | |
linkedType | String | No | |
totalTracks | Integer | No | |
shareableUrlPath | String | No | |
thorLayers | String | No | |
duration | Integer | No | |
unlocked | Boolean | No | |
timeLastUpdated | Timestamp<Epoch<Milliseconds>> | No | |
viewerInfo | JSON object | No | |
autogenForListener | Boolean | No | |
listenerIdInfo | JSON object | No | |
includedTrackTypes | List<Pandora type> | No | |
collectible | Boolean | No | |
listenerId | Integer | No | |
listenerPandoraId | String<Pandora ID> | No | |
listenerIdToken | String | No |
tracks
List
Field | Type | Optional | Default |
---|---|---|---|
itemId | Integer | No | |
pandoraId | String<Pandora ID> | No | |
addedTimestamp | Timestamp<Epoch<Milliseconds>> | No | |
duration | Integer | No | |
trackPandoraId | String<Pandora ID> | No |
viewerInfo
Object
Field | Type | Optional | Default |
---|---|---|---|
editable | Boolean | No |
listenerIdInfo
Object
Field | Type | Optional | Default |
---|---|---|---|
listenerId | Integer | No | |
listenerPandoraId | String<Pandora ID> | No | |
listenerIdToken | String | No |
onDemand.getAudioPlaybackInfo
This method is used to retrieve media URLs for a track.
Example call:
{
"includeAudioToken": true,
"pandoraId": "TR:12345678",
"deviceCode": <v4 UUID from device registration>,
"sourcePandoraId": "AU:12345:123456789012345678:1234567890",
}
The response is fairly self-explanatory. Some things to note:
- The audio data from
audioUrl
is XOR-encrypted. TheaudioToken
is repeated to match the file length. - I'm not sure if the
trackGain
has already been used in processing, or if that is the responsibility of the client. - The skip and receipt URLs are not mandatory, though I'm not sure how they effect recommendations.
Request
onDemand.getAudioPlaybackInfo
Object
Field | Type | Optional | Default |
---|---|---|---|
includeAudioToken | Boolean | No | |
pandoraId | String<Pandora ID> | No | |
deviceCode | String | No | |
sourcePandoraId | String<Pandora ID> | No |
Response
onDemand.getAudioPlaybackInfo
Object
Field | Type | Optional | Default |
---|---|---|---|
audioSkipUrl | String | No | |
audioUrlMap | JSON object | No | |
trackGain | String | No | |
audioReceiptUrl | String | No |
audioUrlMap
Object
Field | Type | Optional | Default |
---|---|---|---|
highQuality | JSON object | No | |
mediumQuality | JSON object | No | |
lowQuality | JSON object | No |
highQuality
Object
Field | Type | Optional | Default |
---|---|---|---|
trackToken | String | No | |
audioToken | String | No | |
bitrate | String | No | |
encoding | String | No | |
audioUrl | String | No |
mediumQuality
Object
Field | Type | Optional | Default |
---|---|---|---|
trackToken | String | No | |
audioToken | String | No | |
bitrate | String | No | |
encoding | String | No | |
audioUrl | String | No |
lowQuality
Object
Field | Type | Optional | Default |
---|---|---|---|
trackToken | String | No | |
audioToken | String | No | |
bitrate | String | No | |
encoding | String | No | |
audioUrl | String | No |
I hope this helps!
I'm free to chat on Matrix, Telegram or Discord too (my username is the same everywhere).
@hacker1024 Hi! I'm glad to see you are still around! Your horn should be tooted, I've been looking at your code ... it's all very impressive! I have been working on getting your pandora_mitm project working but I know NOTHING about Dart which has been holding me back a bit. I also ran into a apktook issue trying to bypass certificate pining (iBotPeaches/Apktool#3718) with 2107.1 but it worked with 1812.2. I have been able to play songs from playlists, albums and podcasts with hardcoded test code. I'm currently in the process of implementing them properly in pianobar. I'm not happy with the way I'm enumerating albums and podcasts currently, it's very inefficient (using collections.v7.getItems and then catalog.v4.annotateObjects). I would like to sniff the official App to see what it does. Any chance you can resurrect your https://crypto.epimetheus.tk/ ? I'm a bit of a discord novice, what server do you hang out on? I'm Skip on the OpenEPaperLink discord channel (https://discord.com/invite/fekcBc5RN5). |
The decryption tool is revived. It is available through its GitHub Pages URL. |
Thanks! |
Is it possible to support playlists? (And maybe create a station from a playlist after it ends?).
The text was updated successfully, but these errors were encountered: