Giter Site home page Giter Site logo

simon-weber / gmusicapi Goto Github PK

View Code? Open in Web Editor NEW
2.5K 134.0 262.0 4.05 MB

An unofficial client library for Google Music.

Home Page: https://unofficial-google-music-api.readthedocs.io

License: BSD 3-Clause "New" or "Revised" License

Python 100.00%
python google google-music client-lib python-3 python-2 gmusicapi

gmusicapi's People

Contributors

adhamu avatar christopher-dg avatar dpogue avatar enigmacurry avatar foreverguest avatar glewtimo avatar gumho avatar hechtus avatar jamon avatar jimyx17 avatar jodal avatar jon-sanders avatar juanrubio avatar karlak avatar mhlinder avatar ocherny avatar orf avatar queengooborg avatar rraval avatar rweichler avatar sauyon avatar sawasy avatar siebert avatar simon-weber avatar soulfx avatar stetro avatar thebigmunch avatar tjstum avatar tyris avatar zachreizner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gmusicapi's Issues

Local transcoding for non-mp3 files

I assumed that MM did transcoding locally for non-mp3 tracks, but this was never verified. @mgillespie believes it's done server-side.

This should be investigated - it would drop a dependency and remove a lot of logic.

Python 2.5 compatibility

This is more of a question than an issue.
Unofficial Google Music API uses some Python 2.6/2.7 specific features like collections.mapping module, except Foo as bar syntax, context managers and named tuples. Due to this it's Python 2.5 incompatible so it cannot run under Maemo 5 (Meego Harmattan as well).

I was wondering what could be done with this problem.
I don't except a refactor for Python 2.5 as it'd really be a regression -- it's only natural to write for Python 2.7/3.x.
The only polite solution I have right now is to wait for the stable version of the API and then make a fork to introduce these incompatible changes.

Failed to validate field 'playlists' list schema

Hi, I ran across thunner the other day and wanted to try it out. I'm getting the following error in the gmusicapi.log file:

2012-12-26 19:07:44,537 - gmusicapi.Api - WARNING - Received an unexpected response from call loadalltracks.

... a gigantic response object...

2012-12-26 19:07:44,551 - gmusicapi.Api - DEBUG - failed schema: {'additionalProperties': {'continuationToken': {'type': 'string'}}, 'type': 'object', 'properties': {'continuation': {'type': 'boolean'}, 'playlist': {'items': {'additionalProperties': False, 'type': 'object', 'properties': {'comment': {'type': 'string', 'blank': True}, 'rating': {'type': 'integer'}, 'lastPlayed': {'required': False, 'type': 'integer'}, 'disc': {'required': False, 'type': 'integer'}, 'matchedId': {'type': 'string', 'blank': True}, 'composer': {'type': 'string', 'blank': True}, 'year': {'required': False, 'type': 'integer'}, 'id': {'type': 'string', 'blank': True}, 'subjectToCuration': {'type': 'boolean'}, 'album': {'type': 'string', 'blank': True}, 'playlistEntryId': {'required': False, 'type': 'string', 'blank': True}, 'title': {'type': 'string', 'blank': True}, 'deleted': {'type': 'boolean'}, 'albumArtist': {'type': 'string', 'blank': True}, 'durationMillis': {'type': 'integer'}, 'type': {'type': 'integer'}, 'titleNorm': {'type': 'string', 'blank': True}, 'track': {'required': False, 'type': 'integer'}, 'storeId': {'required': False, 'type': 'string', 'blank': True}, 'albumArtistNorm': {'type': 'string', 'blank': True}, 'totalTracks': {'required': False, 'type': 'integer'}, 'beatsPerMinute': {'type': 'integer'}, 'genre': {'type': 'string', 'blank': True}, 'playCount': {'type': 'integer'}, 'creationDate': {'type': 'integer'}, 'name': {'type': 'string', 'blank': True}, 'albumNorm': {'type': 'string', 'blank': True}, 'artist': {'type': 'string', 'blank': True}, 'url': {'type': 'string', 'blank': True}, 'totalDiscs': {'required': False, 'type': 'integer'}, 'albumArtUrl': {'required': False, 'type': 'string', 'blank': True}, 'artistNorm': {'type': 'string', 'blank': True}}}, 'type': 'array'}, 'playlistId': {'type': 'string'}, 'differentialUpdate': {'type': 'boolean'}, 'requestTime': {'type': 'integer'}}}
2012-12-26 19:07:44,552 - gmusicapi.Api - WARNING - error was: Failed to validate field 'playlist' list schema: additional properties not defined by 'properties' are not allowed in list item

'gigantic response object' has all of my tracks in it, which I don't really want to post here, but it's a dict containing the following (top-level) keys:

  • playlist
  • requestTime
  • continuation
  • playlistId
  • continuationToken
  • differentialUpdate

This same error occurs when I run the gmusicapi.test.integration_test_api.

This same error occurs for the 'loadplaylist' call as well.

I'm in the US if that matters.

I'm currently trying to debug it myself, but I'm not very familiar with this code yet.

Thanks!

Support new metadata fields

Two new fields have appeared: subjectToCuration and metajamId. Their purpose and requirements are unknown.

All of my tracks have False for curation, and a majority have a 26 character alphanumberic for the mjid.

Support multiple playlists of the same name

Currently, get_playlists returns a mapping of {playlist name: playlist id}. Apparently, multiple playlists of the same name are allowed; get_playlists needs to be changed to support this.

Since this seems to be an edge case, it makes sense to keep mapping names -> single ids when possible. Perhaps an option to always return lists would be a good compromise, for when someone doesn't know anything about the library they're working with.

Support album art mutation

Album art can be changed through the web interface, and should be a fairly straightforward feature to add. I don't have the time to implement it, though.

MP3 files with no, or bad, metadata fail to Upload

Great job on the project.

Some of my mp3 files have no metadata. I've notice that those files fail to upload. See the traceback stack below. I've also noticed that when any kind of exception happens in upload, I can't use it again.

Otherwise, great job!

lateef@neuron ~/Videos> kgmp --add j-cole-visionz-of-home-prod-by-elite-mp3.mp3
'title'
Traceback (most recent call last):
File "/home/lateef/Dropbox/Korin/Korin/Korin/KorinGoogleMusicService/Manager.py", line 69, in add_songs
result = self.gmapi.upload(filenames)
File "", line 2, in upload
File "/home/lateef/Dropbox/Korin/Korin/Korin/KorinGoogleMusicService/gmapi/utils/utils.py", line 65, in wrapper
return function(_args, *_kw)
File "/home/lateef/Dropbox/Korin/Korin/Korin/KorinGoogleMusicService/gmapi/api.py", line 328, in upload
session_requests = self.mm_protocol.make_upload_session_requests(cid_map, metadataresp)
File "/home/lateef/Dropbox/Korin/Korin/Korin/KorinGoogleMusicService/gmapi/protocol.py", line 408, in make_upload_session_requests
"CurrentUploadingTrack": audio["title"][0], #there's an assumption here...
File "/usr/lib/python2.7/site-packages/mutagen/init.py", line 83, in getitem
if self.tags is None: raise KeyError, key

gmusiapi calls getting unexpected responses

Within the last day or two, method calls to gmusicapi have been throwing errors like this:

2012-05-04 11:00:25,663 - gmusicapi.Api - WARNING - Received an unexpected response from call loadalltracks.
2012-05-04 11:00:25,692 - gmusicapi.Api - WARNING - error was: Failed to validate field 'playlist' list schema: additional properties not defined by 'properties' are not allowed in list item

2012-05-04 11:10:13,506 - gmusicapi.Api - WARNING - Received an unexpected response from call loadplaylist.
2012-05-04 11:10:13,507 - gmusicapi.Api - WARNING - error was: Failed to validate field 'playlist' list schema: additional properties not defined by 'properties' are not allowed in list item

I checked two different accounts (one with a large library and one with a small one) and see the warnings from both. As shown above, I have seen the calls from loadalltracks and loadplaylist. It does not seem to appear when creating playlists or updating track metadata.

Consistent Api interface and exceptions

Some calls return raw json from the Music server, and others abstract it away. This needs to be made consistent across the Api interface.

In most cases, calls return two things: if they succeeded, and the data that the call operated on. One way to standardize this would be to raise exceptions on failure (there's already code to detect failure across all web client calls), and always return the identity of data that was modified (usually an id of some sort).

This interface should be broad enough to allow for any kind of underlying Session to use it: web client, music manager, skyjam, etc.

It might be desirable to allow a user to configure the api to not raise any exceptions, and only log warnings. Custom context managers may be useful here, too.

Support playlist continuations

The call to loadplaylist returns a continuationToken just like loadalltracks. Currently, Google limits playlists to 1000 tracks, so there will never be a non-empty second continuation.

If the track limit was ever raised so that continuations are needed, get_playlist_songs would fail silently. The code for handling continuations already exists for loadalltracks and should be used to avoid this in the future.

Update Metadata Format Docs

I noticed that 'track' appears to be an optional entity.
When I do a search, if the song was uploaded with no track meta-data than that field is missing in the returned meta-data.

Currently the documentation implies that the field will always be present (which isn't the case).

Load playlists without parsing html

Calling loadplaylists with no json body will return a list of all user playlists and instant mixes, along with ids and tracks. This is a much better approach than the current html parsing used in get_playlists.

Auto playlists (eg "new and recent") are not included.

Support creating instant mixes

Hi,
Is it possible to add functionality for getting the Instant Mixes that Google throws together? I don't mind writing it myself, but I was just wondering if you knew the particular call of the top of your head or something.

Charles

Updates for Google Play

Google Music is now Google Play Music, but the change seems mostly cosmetic from the api's end. All tests pass right now, but there's no guarantee they'll stay this way. Things should be looked in to:

  • urls: web client calls now hit play.google.com/music/services
  • markup:getting playlist ids depends on markup formatting assumptions - it seems this didn't change
  • name: will people be searching for a "google play music" api now? GitHub and RTD don't get ranked for that search right now.

New Parameter - sessionId

I noticed that Google made a change in their API

and now i need to send a session-id every time i send a request, any idea where i get it?

Feature request: Album art sizing

Not really an issue, but I noticed when you change the last parameters in the url, in this case, the parameter s130 to for ex. s400, you'll get an album art image of size 400x400 px. Could this be implemented as a function like api.Api.getAlbumArt(songid,size) ?

It is not supported utf-8 encode?

Hi .
It is very useful library.
But, it seems to not supporting utf-8 encode.

I try upload korean filename. encoded with utf-8
But I get below error
ValueError: '\xed\x95\x9c\xea\xb8\x80.mp3' has type str, but isn't in 7-bit ASCII encoding. Non-ASCII strings must be converted to unicode objects before being added.

also i try unicode filename include korean
I get below error
File "/home/abyss/Unofficial-Google-Music-API/gmusicapi/protocol.py", line 866, in make_upload_session_requests
"content": str(inlined[key]),
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

Coockie not saved

Me again

Suddenly my app Authentication with Google not work and i want to know if they make any change that you notice in the couple days ago.

Fix track/disc number md assumptions

Around line 800 in protocol.py there are assumptions for the formatting of track/disc numbers that will cause a crash in badly formatted cases.

MAC address is not reliable on some devices (eg VPS)

In #51, @fruel and @damariei both upload from a VPS, where the MAC address will change; this registers a device for each upload.

A more static id needs to be used. @damariei suggested the hostid, though I think this will also break if the vm migrates physical machines. Using the public-facing IP may be a better option.

A way to set a user-defined MAC might need to be used as well.

Feature Request: Force stream highest quality MP3

Im getting low quality streams on a 8Mbps internet connection, sounds like 128KBps on my Home Theater speakers, i can tell the difference between streams and my local copies, even the volume is lower.

Possible to implement an option to force stream highest quality stream like in Google Music App on Android?

Tests failed

maxik@power:~$ python -m gmusicapi.test.integration_test_api
Warning: this test suite _might_ modify the library it is run on.
Email: [email protected]        
Password: 
E
======================================================================
ERROR: setUpClass (__main__.TestWCApiCalls)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/gmusicapi/test/integration_test_api.py", line 53, in setUpClass
    super(TestWCApiCalls, cls).setUpClass()
  File "/usr/local/lib/python2.7/dist-packages/gmusicapi/test/utils.py", line 190, in setUpClass
    cls.api = init()
  File "/usr/local/lib/python2.7/dist-packages/gmusicapi/test/utils.py", line 68, in init
    logged_in = api.login(email, password)
  File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 145, in login
    self.session.login(email, password)
  File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 985, in login
    tokenauth.authenticate(self.client)
  File "/usr/local/lib/python2.7/dist-packages/gmusicapi/utils/tokenauth.py", line 117, in authenticate
    err, resp = self._make_request(url, None, headers)
  File "/usr/local/lib/python2.7/dist-packages/gmusicapi/utils/tokenauth.py", line 76, in _make_request
    resp_obj = handler.open(req)
  File "/usr/lib/python2.7/urllib2.py", line 406, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 519, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 438, in error
    result = self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 625, in http_error_302
    return self.parent.open(new, timeout=req.timeout)
  File "/usr/lib/python2.7/urllib2.py", line 406, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 519, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 438, in error
    result = self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 625, in http_error_302
    return self.parent.open(new, timeout=req.timeout)
  File "/usr/lib/python2.7/urllib2.py", line 406, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 519, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 438, in error
    result = self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 625, in http_error_302
    return self.parent.open(new, timeout=req.timeout)
  File "/usr/lib/python2.7/urllib2.py", line 400, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 418, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1215, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1177, in do_open
    raise URLError(err)
URLError: <urlopen error [Errno 101] Network is unreachable>

----------------------------------------------------------------------
Ran 0 tests in 77.587s

FAILED (errors=1)

maxik@power:~/docs/gplay$ python example_play.py 
Email: [email protected]        
Password: 
Success: logged in.
Search Query: epica
2012-07-18 00:01:36,745 - gmusicapi.Api - WARNING - Received an unexpected response from call search.
2012-07-18 00:01:36,748 - gmusicapi.Api - WARNING - error was: Failed to validate field 'artists' list schema: additional properties not defined by 'properties' are not allowed in list item
Nothing found :(

maxik@power:~/docs/gplay$ pip freeze
Warning: cannot find svn location for distribute==0.6.24dev-r0
GnuPGInterface==0.3.2
PIL==1.1.7
apt-xapian-index==0.44
apturl==0.5.1ubuntu3
argparse==1.2.1
chardet==2.0.1
command-not-found==0.2.44
decorator==3.3.3
## FIXME: could not find svn URL in dependency_links for this package:
distribute==0.6.24dev-r0
foobnix==2.5.36
gmusicapi==2012.05.04
httplib2==0.7.2
jockey==0.9.7
keyring==0.7.1
language-selector==0.1
launchpadlib==1.9.12
lazr.restfulclient==0.12.0
lazr.uri==1.0.3
mutagen==1.20
nvidia-common==0.0.0
oauth==1.0.1
pexpect==2.3
protobuf==2.4.1
pycrypto==2.4.1
pycups==1.9.61
pycurl==7.19.0
pysmbc==1.0.13
python-apt==0.8.3ubuntu7
python-debian==0.1.21ubuntu1
pyudev==0.13
pyxdg==0.19
reportlab==2.5
simplejson==2.3.2
synaptiks==0.8.1
ufw==0.31.1-1
unattended-upgrades==0.1
usb-creator==0.2.23
validictory==0.8.3
vboxapi==1.0
wadllib==1.3.0
wsgiref==0.1.2
xkit==0.0.0
zope.interface==3.6.1

Bad login credentials bug

When bad login credentials were given, an invalid exception was raised in clientlogin.py. I put in a stopgap measure to just stop and return instead. This works for now, but throws away any debugging info. If you want to check it out, @dpogue, here's the commit: 40abd39.

Support uploading other audio formats

According to Google's support page, other than mp3, there are four more audio types supported for upload:

  • aac
  • flac
  • ogg
  • wma

However, the actual Music service only supports mp3 and (presumably) wma; the first three formats are transcoded by Music Manager before upload.

Since we can't distribute encoders/decoders, we're going to need to have end-users download things like lame and oggdec. Python Audio Tools has a page on the dependencies they use.

That said, it's probably easiest to assume whatever tools we need are in the path, and call out to them with subprocess. With that decision made, it should be fairly easy to support other formats: just transcode them in a way that preserves metadata, then use our existing mp3 support.

Safer metadata mutation

Api should expose a way to safely mutate metadata, by specifying which keys are changing. The underlying call does not require an entire song each time, just the keys that are changing.

Allow Metadata_Expectations to support unknown keys

Currently, requesting the expectation of an unknown key will cause an exception:

api.change_song_metadata({'foo':'bar'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 2, in change_song_metadata
  File "gmusicapi/utils/utils.py", line 70, in wrapper
    return function(*args, **kw)
  File "gmusicapi/api.py", line 177, in change_song_metadata
    return self._wc_call("modifyentries", songs)
  File "gmusicapi/api.py", line 564, in _wc_call
    body, res_schema = protocol.build_transaction(*args)
  File "gmusicapi/protocol.py", line 542, in build_transaction
    allowed_values = Metadata_Expectations.get_expectation(key).allowed_values
  File "gmusicapi/protocol.py", line 158, in get_expectation
    expt = getattr(cls, "gm_"+key)
AttributeError: class Metadata_Expectations has no attribute 'gm_foo'

There should be a flexible default expectation to prevent future breakage when metadata keys are added. There should still be a warning, however: something like "key [name] is not known - using default expectation".

Unified data model

@dpogue: I've been thinking of a building a unified data model lately, that I think may simplify implementation and ease some of the concerns with mutation and building dictionaries. The basic idea is that we'd have a single collection of all the tracks/playlists backed with the actual representations from Google Music. The Song and Playlist models would basically be pointers into this central collection, keeping everything in sync across models. It would look something like this:

Library

This is the main collection of all the songs and playlists.

It's backed by two privateish dictionaries: {"song id": {<GM song dictionary>}} and {"playlist id": [("song id", "entryId"), ...]}. It also exposes methods for searching and retrieving songs/playlists (I've already got some things in gmtools that'll come in handy here for searching locally. There's also the slower search() that hits the server.).

I'll show how we'd handle mutation and updates later, but my idea is that Songs and Playlists expose methods that simply change the GM representations in the Library. That way, we can leave it up to the user at runtime whether they want to auto-update or batch their changes. Keeping everything in their GM representation is a big help there; we'd just have to track which ids changed, and then push their current representation. (only thing: I'm not actually sure what we have to work with in the way of pushing playlist changes, but worst case we can always erase and create a new one with the same name).

Song

This represents a single song in the Library. The only data member is the id string, which is used to index into the Library dictionary when metadata is requested.

Metadata changes could either be done by indexing into a song:

song['artist'] = 'foo'
print song['album']

or by properties. That's just a choice between hooking __getitem__ or __getattribute__.

Getting/setting metadata goes through the Library to keep everything in sync, which I think makes more sense than being able to have different instances of songs:

song = library.songs["id"] # not direct access to the underlying 
                           # dictionaries; we'd control this. maybe
                           # a get() that support lists would be better

same_song = library.songs["id"] # returns a new Song instance

assert song.id() != same_song.id() # id() being the Python id (memory location)
assert song == same_song           # comparison/hashing is just done with GM ids

print song["artist"] # => "old artist"
song["artist"] = "new artist"
print same_song["artist"] # => "new artist"

The setters in Song (or Library, which is probably better) would hook into the Metadata_Expectations to ensure we're only setting mutable things to the allowed values, etc.

Playlist

Playlists are a lot like songs; they'd just store their playlistId, and modify the Library collection. Tools for changing them are exposed just like songs.

Mutation

Songs and Playlists wouldn't be directly manipulating the dictionaries that back Library. There'd be one level (or two, if we want to check change validity in Library instead of Song) of indirection, which handles whether or not we want to batch changes or update everything on every change. Batching changes would just mean maintaining a set of all the song/playlist ids that have been modified.

Either way, when an update occurs, we flush out all our changes and verify they went ok (metadata poses a problem, since the server doesn't actually accurately tell us if our request succeeded. I'm not really sure how to deal with this. Right now, when I test, I'm pulling down the entire library to see my changes - not ideal for large libraries. I'm thinking an optimistic approach would be best here: assume we succeeded, and fork a thread to verify at some later time that we actually did. If not, either try again or warn/raise an exception that the Library is out of date. That's just details, though.).

Assuming away the metadata verification for now, the entire process looks something like:

  • user sets some piece of data in a Song, eg song["artist"] = "foo"
  • Song checks to see that "foo" is allowed for artist, and that artist is mutable
  • it is, so it submits a change to Library (who assumes the change is valid - it's probably better to do verification of change validity in Library instead...)
  • Library gets a change.
    • change recorded in the internal dictionary
    • ** if we're auto-updating: **
      • change pushed out to GM.
      • we block until we see the change was valid
    • ** if we're batching: **
      • we record the id as dirty
      • at some later date, changes are pushed; we push out everything that's got a dirty id and verify all changes were good.

Whew, that was a bit more than I thought it would - which brings me to my next point!

I'm afraid this might be a bit heavy to build in to Api. Maybe we can provide it as another abstraction layer on top of Api? This helps with two things:

  • it's not a destructive change for people working with Api right now
  • it shouldn't matter how we're pushing changes (skyjam vs web client); the model doesn't care and Api can decide

Let me know what you think! I'll have plenty of time next week to work on this if/when we decide on something.

JavaScript port

Hello,
first thanks for developing this API !

I wanted to know how hard is it to port the code to JavaScript, since I want to use it in Nightingale (an app based of Mozilla framework).
Is it something that could be achieved easily ?

estimatedSize?

I saw you mention a property in the SkyJam docs called estimatedSize - I haven't seen that property yet on the JSON that Google returns, and it is not written current doc. Did it disappear from the API or what happened to it?

Make gmapi API usable from any Python folder or package

Thanks for gmapi, it's awesome and I'm actually building an App around it.

So the issue is that gmapi is not currently usable from an Python folder or Package other than if it is installed in the default site-packages folder. This is a reasonable assumption but for people who are testing the API or just want to bundle the API in their packages or apps, this poses a problem.

The problem is solved by not using global imports in the source files, but using relative imports instead. For example instead of

from gmapi.session import WC_Session, MM_Session

this is preferable

from session import WC_Session, MM_Session

This way, I can use put the gmapi folder in any python package or folder that has init.py and use it without problems like I do in my project.

Thanks for gmapi and keep up the great work!

Question: ok to require Music Manager for uploading?

@mgillespie, @damariei, @fruel, and anyone else:

I want uploading to work again, and more reliably. I've been thinking of changing direction and using the Music Manager instead of trying to replace it. I'd still be able to provide the same interface as now, I think.

So, a question for you folks: is requiring an installed Music Manager a dealbreaker? I don't think it would be a problem to get everything working on a headless box.

The big gains for you would be scan and match, faster uploads and fewer breakages. On my end: (hopefully) less scrambling on protocol changes, and a compliant client.

Personally, I think it would be worth it, but I don't want to make a big switch without getting some opinions first. So, let me know what you think.

Also, happy holidays! =)

Stricter response validation

At the moment, only the structure and type of the responses are validated in protocol.WC_Protocol. However, Validictory has a whole host of other tools to use (some undocumented), and the stricter validation is, the sooner a protocol change would become apparent.

Some simple things to validate:

  • for playlists, continuation should always be False - this tests the playlist limit assumption
  • any kind of id (song, playlist, entry) can be matched against a regex, like is currently done in test.utils

Also, it's probably smart to prompt end-users to submit an issue when validations fail.

Uploading broken: DecodeError, BadStatusLine, session response empty

Reports that it's uploaded correctly, but it's missed everything in my library.

Running the tests, I see the following:

2012-11-21 22:08:45,573 - gmusicapi.UnitTestedApi - WARNING - Received an unexpected response from call loadalltracks.
2012-11-21 22:08:45,821 - gmusicapi.UnitTestedApi - WARNING - error was: Failed to validate field 'playlist' list schema: additional properties not defined by 'properties' are not allowed in list item
.2012-11-21 22:09:55,008 - gmusicapi.UnitTestedApi - WARNING - Received an unexpected response from call loadalltracks.
2012-11-21 22:09:55,250 - gmusicapi.UnitTestedApi - WARNING - error was: Failed to validate field 'playlist' list schema: additional properties not defined by 'properties' are not allowed in list item
2012-11-21 22:10:56,259 - gmusicapi.UnitTestedApi - WARNING - Received an unexpected response from call loadalltracks.
2012-11-21 22:10:56,488 - gmusicapi.UnitTestedApi - WARNING - error was: Failed to validate field 'playlist' list schema: additional properties not defined by 'properties' are not allowed in list item

....E

ERROR: test_up_deletion (main.TestWCApiCalls)

Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/test/integration_test_api.py", line 211, in test_up_deletion
self.run_steps("updel_")
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/test/utils.py", line 234, in run_steps
step()
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/test/integration_test_api.py", line 185, in updel_1_upload
result = self.api.upload(some_files)
File "", line 2, in upload
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/utils/utils.py", line 136, in wrapper
return function(_args, *_kw)
File "", line 2, in upload
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/utils/utils.py", line 113, in wrapper
return function(_args, *_kw)
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 690, in upload
fname_to_id = self._upload_mp3s(map(lambda f: f.name, upload_files))
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 781, in _upload_mp3s
metadataresp = self._mm_pb_call("metadata", metadata_request)
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 887, in _mm_pb_call
res.ParseFromString(self.session.post_protobuf(url, req))
File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 1058, in post_protobuf
resp = self.android.getresponse()
File "/usr/lib/python2.7/httplib.py", line 1030, in getresponse
response.begin()
File "/usr/lib/python2.7/httplib.py", line 407, in begin
version, status, reason = self._read_status()
File "/usr/lib/python2.7/httplib.py", line 371, in _read_status
raise BadStatusLine(line)
BadStatusLine: ''


Ran 6 tests in 298.702s

FAILED (errors=1)

Application specific password or two-factor auth

This is not a feature request per se.
I'd like to know whether it's (or will be) possible to use application specific password like the Google Music Manager does in case user has two-factor auth enabled.
The question is more like, whether the API can handle it (currently it throws an exception)?

Error in make_upload_session_requests

I tried to use your API to upload mp3s to Google Music but calling api.upload always returns an empty dictionary.

My upload script: http://pastebin.com/b7r7GPNj
I run it with this command: python google_music.py -e [email protected] /x/y/z/This\ is\ a\ MP3.mp3

MP3 file: http://pastebin.com/WCUWg5Ub

I have 510 songs on my Google Music account and the the device I want to use for uploading is authorized.

I traced the problem to the following lines:

protocol.py:841 (make_upload_session_requests): The array server_response.response.uploads is empty.

api.py:781 (_upload_mp3s): self._mm_pb_call("metadata", metadata_request) returns this:
u0: 1
response {
}
state {
u0: 0
u1: 0
u2: 5
u3: 12000
u4: 0
u5: 16000
}

Until last week I used https://github.com/antimatter15/google-music-protocol/blob/master/py/upload.py to upload mp3s to Google Music but this script stopped working last week. Runnig it in verbose mode it prints the same response as your API (self._mm_pb_call("metadata", metadata_request) )

Support proxies

This would be an opportunity to overhaul the terrible PlaySession code by moving to Requests (which would also be a good time for authentication changes, eg #48).

Transcoded flac files will not play online

For some flacs (not all unfortunately), the transcoded versions will not play on the google play music page. Nor will they play on my android devices. Not sure how to debug this, but let me know.

ffmpeg version 1.0.1 Copyright (c) 2000-2012 the FFmpeg developers
built on Dec 4 2012 13:43:06 with gcc 4.7 (Debian 4.7.2-4)
configuration: --prefix=/usr --extra-cflags='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security ' --extra-ldflags='-Wl,-z,relro' --cc='ccache cc' --enable-shared --enable-libmp3lame --enable-gpl --enable-nonfree --disable-decoder=libdirac --enable-libvorbis --enable-pthreads --enable-libfaac --enable-libxvid --enable-postproc --enable-x11grab --enable-libgsm --enable-libtheora --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libx264 --enable-libspeex --enable-nonfree --disable-stripping --enable-libschroedinger --disable-encoder=libschroedinger --enable-version3 --enable-libopenjpeg --enable-libvpx --enable-librtmp --enable-avfilter --enable-libfreetype --enable-libvo-aacenc --disable-decoder=amrnb --enable-libvo-amrwbenc --enable-libaacplus --libdir=/usr/lib/x86_64-linux-gnu --disable-vda --enable-libbluray --enable-libcdio --enable-gnutls --enable-frei0r --enable-openssl --enable-libass --enable-libopus --enable-fontconfig --enable-libdc1394 --disable-altive libavutil 51. 73.101 / 51. 73.101
libavcodec 54. 59.100 / 54. 59.100
libavformat 54. 29.104 / 54. 29.104
libavdevice 54. 2.101 / 54. 2.101
libavfilter 3. 17.100 / 3. 17.100
libswscale 2. 1.101 / 2. 1.101
libswresample 0. 15.100 / 0. 15.100
libpostproc 52. 0.100 / 52. 0.100

DecodeError when trying to Upload.

While testing my application which uploads files using the API, I seemingly at random started to get the following issue when doing api.upload('file.mp3'):

  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<string>", line 2, in upload
    File "/usr/local/lib/python2.7/dist-packages/gmusicapi/utils/utils.py", line 136, in wrapper
      return function(*args, **kw)
    File "<string>", line 2, in upload
    File "/usr/local/lib/python2.7/dist-packages/gmusicapi/utils/utils.py", line 113, in wrapper
      return function(*args, **kw)
    File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 690, in upload
      fname_to_id = self._upload_mp3s(map(lambda f: f.name, upload_files))
    File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 781, in _upload_mp3s
      metadataresp = self._mm_pb_call("metadata", metadata_request)
    File "/usr/local/lib/python2.7/dist-packages/gmusicapi/api.py", line 887, in _mm_pb_call
      res.ParseFromString(self.session.post_protobuf(url, req))
    File "/usr/local/lib/python2.7/dist-packages/google/protobuf/message.py", line 179, in ParseFromString
      self.MergeFromString(serialized)
    File "/usr/local/lib/python2.7/dist-packages/google/protobuf/internal/python_message.py", line 758, in MergeFromString
      raise message_mod.DecodeError('Unexpected end-group tag.')
  google.protobuf.message.DecodeError: Unexpected end-group tag.

I have not been able to make it go away after trying with multiple files.

Any ideas?

Code Base Overview

The current code base needs an overview to get future contributors up to speed; I'll take care of it.

Packaging

The project should be packaged using pip and be submitted to PyPI. Read the Docs supports pip dependencies, so this has the added benefit of cleaning up the mock-out mess that currently exists.

__all__ should be used to export only relevant modules/classes.

Possible license change

@dpogue @antimatter15 @mystilleef @gumho (I also just realized antimatter15 isn't in the AUTHORS file - I'll fix that)

Hey, all. Everyone above has contributed to the project in some way or another. I'm thinking about a possible license change from GPLv3, and my understanding is that you would all have to agree to it before I changed it.

My original reasoning for picking the GPL was simply to choose the most restrictive license I could. I intend the project to be a value-add for Google, and I thought restricting use would also prevent abuse.

Today, I'm not as concerned about this, since the api really doesn't enable more than Google's end-user tools do. So, I'm proposing a switch to the 3-clause BSD license. The license change is to address two things that the GPL doesn't do:

  • anti-endorsement: a concern since I'll be working for Google - no one will be able to say it was "written by a Google intern"
  • allow linking from other licenses: according to RMS, you can't even import GPL code if your code is not GPL. I'd prefer this not be the case.

Any thoughts? If you agree, just leave a comment saying to go ahead with the change.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.