forked from simon-weber/gmusicapi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
protocol_info
161 lines (125 loc) · 5.03 KB
/
protocol_info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
Protocol Information:
#-------------------------------------------------#
This file is obsolete, and might be out of date.
Check protocol.py for current information.
#-------------------------------------------------#
Calls are made to music.google.com/music/services/<call name>
Calls also need to send 'u' (I've only seen it =0) and 'xt' (=the same as the cookie) in the url. A sample url:
https://music.google.com/music/services/search?u=0&xt=AM-WbXjYA_Y1LgZx_znahg9rOeIg3aDtWg:<long number>
== Implemented Calls ==
req - request
res - response on a success
addplaylist:
req: {"title": "<name>"}
res: {"id":"<new playlist id>","title":"<name>","success":true}
addtoplaylist:
req: {"playlistId":"<playlist to add to>","songIds":["<songs to add>"]}
res: {"playlistId":"<same as above>","songIds":[{"playlistEntryId":"<new id>","songId":"<same as above>"}]}
deleteplaylist:
req: {"id": "<playlist to delete>"}
res: {"deleteID": "<id>"}
deletesong:
delete from library:
req: {"songIds": ["<id1>", "<id2>"], "entryIds":[""], "listId": "all"}
res: {"listId":"all","deleteIds":["<id1>"]}
delete from playlist:
same as from library, but given entryIds (from loadplaylist) and a listId.
loadalltracks:
Libraries can be big, so GM sends the tracks down in 'chunks'.
Requests that don't complete the library have 'continuation tokens' required to get the next chunk.
req:
first: {}
continuations: {"continuationToken":"<token>"}
res:
{"continuation": <True or False>,
"continuationToken": "<token if there is a next chunk>",
"differentialUpdate": <False - never seen True>,
"playlist": [ {<song 1>}, {<song 2>}... ],
"playlistID": "all",
"requestTime": <some time representation>
}
loadplaylist:
Loads the songs from a playlist.
req: {"id": "<playlist id>"}
res: {"continuation": False, //never seen true, but my playlists are small. likely works like loadalltracks
"playlist": [{<song 1>}, {<song 2>}], //songs also include an entryId
"playlistId": "<id>",
"unavailableTrackCount": <number>}
modifyentries:
Edits song metadata.
There are a lot of things to be careful of when editing metadata.
Everything known is in protocol.py.WC_Protocol under Metadata Expectations.
You should be safe if you stick to changing these keys:
rating to one of:
0 - no thumb
1 - down thumb
5 - up thumb
name - _don't_ use title. title is reset to whatever name is.
album
albumArtist
artist
composer
disc
genre
playCount
totalDiscs
totalTracks
track
year
Also note that the server response is _not_ to be trusted.
Reload the entire library, then re-read tracks to see updates.
req: {"entries":[ {<song 1>}, ...]}
res: {"songs":[ {<your _exact_ request for song 1; _not_ what's on the server>}, ...],
"success":true}
modifyplaylist:
Changes the title of a playlist.
req: {"playlistId": "<id>", "playlistName": "<title to change to>"}
res: {} - note the lack of success entry here; this is normal.
multidownload:
Gets download links. Only tested with 1 id, but it likely supports multiple.
req: {"songIds":[<id>"]}
res: {"downloadCounts": {"<id>":<times it has been downloaded>},
"url":"<download url>"}
play:
Gets a url that holds a streamable copy of this song. Authentication is _not_ needed to stream this url.
This call is unique in that it does not use /services. Its complete url is music.google.com/music/play
In addition, it has an empty request body. The songid is passed in the querystring, along with u=0 and pt=e (not sure what pt signifies).
req: <completely empty>
res: {"url":"<url that holds audio file>"}
search:
req: {"q": "<query>"}
res: {"results":{"artists":[<hits>],"albums":[<hits>],"songs":[<hits>]}}
== Song Metadata Example ==
(see protocol.WC_Protocol metadata expectations for more information)
Song metadata is sent in dictionaries.
All songs in my library have either 27 or 28 keys. Here's an example:
{'comment': ''
'rating': 0
'lastPlayed': 1324954872637533L
'disc': 1
'composer': ''
'year': 2009
'id': '305a7b83-32fa-3a71-9a77-498dfce74aad'
'album': 'Live on Earth'
'title': 'The Car Song'
'deleted': False
'albumArtist': 'The Cat Empire'
'type': 2
'titleNorm': 'the car song'
'track': 2
'albumArtistNorm': 'the cat empire'
'totalTracks': 0
'beatsPerMinute': 0
'genre': 'Alternative'
'playCount': 0
'creationDate': 1324614519429366L
'name': 'The Car Song'
'albumNorm': 'live on earth'
'artist': 'The Cat Empire'
'url': ''
'totalDiscs': 2
'durationMillis': 562000
'artistNorm': 'the cat empire',
(optional entry; exists if there is album art)
'albumArtUrl': '//lh6.googleusercontent.com/<long identifier>'
}