Open roosemberth opened 6 years ago
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. browse. pods.*
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.
Update on this issue: Playlists are supported my the mobile API (as expected). The following API calls are probobly relevant:
playlists. browse. pods.*
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.
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
The Pandora REST API is used by modern Pandora apps including the website and the various mobile apps provided by Pandora.
Does that mean that the APK you used is of an old app that's no longer distributed ?
What is the status of https://github.com/PromyLOPh/pandora-apidoc/issues/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 ./com/pandora/radio/api/PublicApi.java
is the code which talks to the (json) tuner API.
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 adb shell pm path com.pandora.android
to get the path to the APK, then adb pull <PATH>
to pull the APK (remove the package: prefix from the path).
@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:
0) q 60's Hippie Band Radio
1) P 60's Hippie Band Radio Thumbs Up
2) q 60s Oldies Radio
3) q 60s Rock Radio
4) q 60s, 70s, and 80s Hits Radio
5) q 70s Funk Radio
6) P 70s Funk Radio Thumbs Up
7) q 70s Pop Radio
8) q 70s Remix Radio
9) q 70s Rock Radio
10) q 80s Dance Radio
11) q 80s Pop Radio
12) q 80s Rock Ballads Radio
13) q 90s Dance Radio
14) q African Radio
15) q Artie Shaw & His Orchestra Radio
16) q Average White Band Radio
17) q aydio Radio
18) q Bach: Solo Piano
19) q Beach Bar Lounge Radio
20) P Best Synthesizer Soundtracks for Films
21) q Betrayal (Sorcerer Theme) Radio
22) q Blues Rock Radio
23) q Bob Dylan Radio
24) q Bob Marley Radio
25) q Bobby Womack Radio
26) q Boom Boom (2007 Remastered Version) Radio
27) q Brent Lewis Radio
28) q Brewer & Shipley Radio
29) q Cat Stevens Radio
30) q Celtic Radio
31) q Chicago Blues Radio
32) q Chris Stapleton Radio
33) q Christmas Choral Classics Radio
34) q Christmas Traditional Radio
35) q Classic Rock Radio
36) q Classical Guitar Radio
37) q Creedence Clearwater Revival Radio
38) q Crosby, Stills & Nash Radio
39) P Crosby, Stills & Nash Radio Thumbs Up
40) q Crosby, Stills, Nash & Young Radio
41) P Crosby, Stills, Nash & Young Radio Thumbs Up
42) q Delbert McClinton Radio
43) q Delta Blues Radio
44) q Dire Straits Radio
45) q Disco Radio
46) q Eagles Radio
47) P Electronic
48) q Eric Church Radio
49) q Eric Clapton Radio
50) q Evelyn "Champagne" King Radio
51) q Frightening Sounds Radio
52) q Gary Allan Radio
53) q Gary Moore Radio
54) P Gary Moore Radio Thumbs Up
55) q Gerry Rafferty Radio
56) q Gil Scott-Heron Radio
57) q Golden Oldies Radio
58) q Gordon Lightfoot Radio
59) q Halloween Party Radio
60) q Handpicked 100: Scary Shuffle Radio
61) q Hot Tuna Radio
62) q Instrumental Chill Radio
63) q Jackson Browne & David Lindley Radio
64) q Jackson Browne Radio
65) q James Taylor Radio
66) q Jamey Johnson Radio
67) q Joan Baez Radio
68) q Joe Bonamassa Radio
69) q John Williams (Classical Guitar) Radio
70) q Lenny Radio
71) q Lowrider Oldies Radio
72) q Meditation Radio
73) q Michael Jackson Radio
74) q Motown Radio
75) P My Thumbs Up
76) q Neil Young Radio
77) q New Age Instrumental Radio
78) q Nick Cave & The Bad Seeds Radio
79) q Nick Moss Band Radio
80) q Oldies Party Radio
81) P Oldies Party Radio Thumbs Up
82) q Paul Whiteman & His Orchestra Radio
83) q People Are Strange (Mono; 2017 Remaster) Radio
84) q Pharrell Williams Radio
85) q Pink Floyd Radio
86) Q QuickMix
87) q Reggaeton Old School Radio
88) q Rock 'n' Roll Dad Radio
89) q Rock Hits Radio
90) q Santana Radio
91) q Scottish Traditional Radio
92) q Simon & Garfunkel Radio
93) q Smooth Jazz Instrumentals Radio
94) q Spooky Symphonies Radio
95) q St. Patrick's Day Radio
96) q Stevie Ray Vaughan Radio
97) q Sugar Ray Radio
98) q Taylor Swift Radio
99) q Teddy Pendergrass Radio
100) q Teena Marie Radio
101) q The Band Radio
102) q The Beatles Radio
103) q The Cambridge Singers (Holiday) Radio
104) q The Doobie Brothers Radio
105) q The Doors Radio
106) P The Drop
107) q The Glenn Miller Orchestra Radio
108) q The Kinks Radio
109) q The Mamas & The Papas Radio
110) q The Marshall Tucker Band Radio
111) q The Righteous Brothers Radio
112) q The Staple Singers Radio
113) Thumbprint Radio
114) P Top Dance Hits
115) q Van Morrison Radio
116) q Vom Himmel Hoch Radio
117) P Werewolf Music - Full Moon
118) q Whiskey Myers Radio
119) P Your Blues Soundtrack
120) P Your Classic Rock Soundtrack
121) P Your Focus Soundtrack
122) P Your Party Soundtrack
123) P Your Rock & Roll Soundtrack
[?] Select station:
And this is what the it looks like in playlist mode:
0) P 60's Hippie Band Radio Thumbs Up
1) P 70s Funk Radio Thumbs Up
2) P Best Synthesizer Soundtracks for Films
3) P Crosby, Stills & Nash Radio Thumbs Up
4) P Crosby, Stills, Nash & Young Radio Thumbs Up
5) P Electronic
6) P Gary Moore Radio Thumbs Up
7) P My Thumbs Up
8) P Oldies Party Radio Thumbs Up
9) P The Drop
10) P Top Dance Hits
11) P Werewolf Music - Full Moon
12) P Your Blues Soundtrack
13) P Your Classic Rock Soundtrack
14) P Your Focus Soundtrack
15) P Your Party Soundtrack
16) P Your Rock & Roll Soundtrack
P = playlist
@skiphansen
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:
playlists.v7.getTracks
This method returns the list of tracks in the given playlist.
Example call:
{
"request": {
"limit": 1000,
"annotationLimit": 20,
"pandoraId": "PL:123456789012345678:1234567890",
"playlistVersion": 2,
"bypassPrivacyRules": true
}
}
Object
Field | Type | Optional | Default |
---|---|---|---|
request | JSON object | No |
Object
Field | Type | Optional | Default |
---|---|---|---|
limit | Integer | No | |
annotationLimit | Integer | No | |
pandoraId | String\ |
No | |
bypassPrivacyRules | Boolean | No | |
playlistVersion | Integer | Yes |
Object
Field | Type | Optional | Default |
---|---|---|---|
notModified | Boolean | No | |
tracks | List\ |
Yes | |
offset | Integer | Yes | |
annotations | Map\<String\ |
No | |
pandoraId | String\ |
No | |
version | Integer | No | |
name | String | No | |
description | String | No | |
timeCreated | Timestamp\<Epoch\ |
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\ |
No | |
viewerInfo | JSON object | No | |
autogenForListener | Boolean | No | |
listenerIdInfo | JSON object | No | |
includedTrackTypes | List\ |
No | |
collectible | Boolean | No | |
listenerId | Integer | No | |
listenerPandoraId | String\ |
No | |
listenerIdToken | String | No |
List
Field | Type | Optional | Default |
---|---|---|---|
itemId | Integer | No | |
pandoraId | String\ |
No | |
addedTimestamp | Timestamp\<Epoch\ |
No | |
duration | Integer | No | |
trackPandoraId | String\ |
No |
Object
Field | Type | Optional | Default |
---|---|---|---|
editable | Boolean | No |
Object
Field | Type | Optional | Default |
---|---|---|---|
listenerId | Integer | No | |
listenerPandoraId | String\ |
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:
audioUrl
is XOR-encrypted. The audioToken
is repeated to match the file length.trackGain
has already been used in processing, or if that is the responsibility of the client.Object
Field | Type | Optional | Default |
---|---|---|---|
includeAudioToken | Boolean | No | |
pandoraId | String\ |
No | |
deviceCode | String | No | |
sourcePandoraId | String\ |
No |
Object
Field | Type | Optional | Default |
---|---|---|---|
audioSkipUrl | String | No | |
audioUrlMap | JSON object | No | |
trackGain | String | No | |
audioReceiptUrl | String | No |
Object
Field | Type | Optional | Default |
---|---|---|---|
highQuality | JSON object | No | |
mediumQuality | JSON object | No | |
lowQuality | JSON object | No |
Object
Field | Type | Optional | Default |
---|---|---|---|
trackToken | String | No | |
audioToken | String | No | |
bitrate | String | No | |
encoding | String | No | |
audioUrl | String | No |
Object
Field | Type | Optional | Default |
---|---|---|---|
trackToken | String | No | |
audioToken | String | No | |
bitrate | String | No | |
encoding | String | No | |
audioUrl | String | No |
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 (https://github.com/iBotPeaches/Apktool/issues/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?).