Skip to content

Update gmusicapi and changes webclient with mobileclient#13

Closed
pablorusso wants to merge 2 commits intojwdempsey:masterfrom
pablorusso:update_gmusicapi_fix_login
Closed

Update gmusicapi and changes webclient with mobileclient#13
pablorusso wants to merge 2 commits intojwdempsey:masterfrom
pablorusso:update_gmusicapi_fix_login

Conversation

@pablorusso
Copy link

Web protocol was deprecated by google, so I have updated the gmusicapi and changes gmusic to use only mobileclient.

The new gmusicapi depends on pycrypto which was a PITA to build and use inside a windows plex, but its is working. I had tested it only in a windows server.

The trick is to compile it with plex python and vs2013 as plex uses a custom python 2.7 build (plexsripthost) that depednds on msvcrt120 instead of msvcrt90 (like regular python 2.7 does)

@jwdempsey
Copy link
Owner

This is fantastic! I'll give this a try locally and make sure it works in OSX as well.

@gismo112
Copy link

It works nice. Thank you :)

@dpeukert
Copy link

I'm getting this error on PMS v0.9.12.11 and Ubuntu Server 15.04.

2015-08-31 13:20:33,367 (7f18e622f700) :  CRITICAL (core:613) - Exception starting plug-in (most recent call last):

  File "bundles-release/Framework.bundle-dist/Contents/Resources/Versions/2/Python/Framework/core.py", line 606, in start

  File "bundles-release/Framework.bundle-dist/Contents/Resources/Versions/2/Python/Framework/code/sandbox.py", line 256, in execute

  File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Code/__init__.py", line 4, in <module>

    from gmusic import GMusic, CallFailure, API

  File "bundles-release/Framework.bundle-dist/Contents/Resources/Versions/2/Python/Framework/code/sandbox.py", line 345, in __import__

ImportError: cannot import name _counter

@pablorusso
Copy link
Author

The fix includes the binaries only for windows.

For all on Linux: You have to download Crypto from https://pypi.python.org/pypi/pycrypto, and then build it: python setup.py build.

Then copy the Crypto from the "build" directory to replace the Crypto directory.

The entire path to the folder you need to copy is 'build/lib.linux-x86_64-2.7/Crypto'. It needs to go into '/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared'.

Make sure you copy the 'Crypto' folder and not just the contents of it. Also make sure plex has proper access to it by doing 'sudo chown -R plex:plex Crypto' (or whatever user runs your Plex server).

Would be nice if someone on linux send a PR with this binaries to help others in linux.

@r0n0c
Copy link

r0n0c commented Sep 16, 2015

Exact steps on Ubuntu

  1. sudo apt-get install gcc python python-dev
  2. wget https://pypi.python.org/packages/source/p/pycrypto/pycrypto-2.6.1.tar.gz
  3. tar -xf pycrypto-2.6.1.tar.gz
  4. cd pycrypto-2.6.1/
  5. python setup.py build
  6. sudo cp -R build/lib.linux-x86_64-2.7/Crypto /var/lib/plexmediaserver/Library/Application\ Support/Plex\ Media\ Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/
  7. sudo chown -R plex:plex /var/lib/plexmediaserver/Library/Application\ Support/Plex\ Media\ Server/Plug-ins/

After doing this I can get the plugin to load when I don't have a username/password set. I get into it and I get a refresh Library button. After I set login credentials. (I use an a google App password since I have two-factor authentication enabled.) I get the same "This channel is not responding" error.

Log with no credentials set:

2015-09-16 12:33:10,203 (7fec53bde700) : DEBUG (runtime:717) - Handling request GET /music/googlemusic
2015-09-16 12:33:10,204 (7fec53bde700) : DEBUG (runtime:814) - Found route matching /music/googlemusic
2015-09-16 12:33:10,205 (7fec53bde700) : DEBUG (base:117) - Checking if com.plexapp.plugins.googlemusic is broken
2015-09-16 12:33:10,205 (7fec53bde700) : DEBUG (networking:166) - Requesting 'http://127.0.0.1:32400/:/plugins/com.plexapp.system/messaging/function/X1N0b3JlU2VydmljZTpJc0NoYW5uZWxCcm9rZW4_/Y2VyZWFsMQoxCmxpc3QKMApyMAo_/Y2VyZWFsMQoxCmRpY3QKMQpzMzEKY29tLnBsZXhhcHAucGx1Z2lucy5nb29nbGVtdXNpY3MxMAppZGVudGlmaWVycjAK'
2015-09-16 12:33:10,211 (7fec53bde700) : DEBUG (runtime:106) - Sending packed state data (112 bytes)
2015-09-16 12:33:10,211 (7fec53bde700) : DEBUG (runtime:918) - Response: [200] MediaContainer, 751 bytes

Setting credentials and trying to open plugin

2015-09-16 12:34:31,135 (7fec53bde700) : DEBUG (runtime:717) - Handling request GET /music/googlemusic/:/prefs
2015-09-16 12:34:31,136 (7fec53bde700) : DEBUG (runtime:814) - Found route matching /music/googlemusic/:/prefs
2015-09-16 12:34:31,138 (7fec53bde700) : DEBUG (runtime:106) - Sending packed state data (112 bytes)
2015-09-16 12:34:31,138 (7fec53bde700) : DEBUG (runtime:918) - Response: [200] MediaContainer, 363 bytes
2015-09-16 12:34:44,528 (7fec53bde700) : DEBUG (runtime:717) - Handling request GET /music/googlemusic/:/prefs/set?email=EMAILADDRESS&password=THISSHOULDNTBEPLAINTEXT
2015-09-16 12:34:44,529 (7fec53bde700) : DEBUG (runtime:814) - Found route matching /music/googlemusic/:/prefs/set
2015-09-16 12:34:44,530 (7fec53bde700) : DEBUG (preferences:198) - Saved the user preferences
2015-09-16 12:34:44,530 (7fec53bde700) : DEBUG (runtime:106) - Sending packed state data (112 bytes)
2015-09-16 12:34:44,530 (7fec53bde700) : DEBUG (runtime:918) - Response: [200] bool, 0 bytes
2015-09-16 12:34:48,294 (7fec53bde700) : DEBUG (runtime:717) - Handling request GET /music/googlemusic
2015-09-16 12:34:48,296 (7fec53bde700) : DEBUG (runtime:814) - Found route matching /music/googlemusic
2015-09-16 12:34:48,634 (7fec53bde700) : CRITICAL (runtime:883) - Exception (most recent call last):
File "bundles-release/Framework.bundle-dist/Contents/Resources/Versions/2/Python/Framework/components/runtime.py", line 843, in handle_request
File "bundles-release/Framework.bundle-dist/Contents/Resources/Versions/2/Python/Framework/handlers/base.py", line 111, in call
File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Code/init.py", line 47, in MainMenu
API.authenticate(Prefs['email'], Prefs['password'])
File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusic.py", line 66, in authenticate
self._set_all_access()
File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusic.py", line 38, in _set_all_access
settings = self._webclient._make_call(webclient.GetSettings, '')
File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusicapi/clients/shared.py", line 80, in _make_call
return protocol.perform(self.session, self.validate, _args, *_kwargs)
File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusicapi/protocol/shared.py", line 208, in perform
response = session.send(req_kwargs, cls.required_auth)
File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusicapi/session.py", line 81, in send
raise NotLoggedIn
NotLoggedIn

2015-09-16 12:34:48,636 (7fec53bde700) : DEBUG (runtime:106) - Sending packed state data (112 bytes)
2015-09-16 12:34:48,636 (7fec53bde700) : DEBUG (runtime:918) - Response: [500] 1795 bytes

@imthenachoman
Copy link

@r0n0c Were you able to get it to work with those steps?

@r0n0c
Copy link

r0n0c commented Nov 2, 2015

@imthenachoman nope. Kept getting errors.

Then I realized everything I play plex on is either a nexus player or chromecast enabled so I don't need it. And once All Access Family plan is up and running girlfriend won't need it either.

@imthenachoman
Copy link

@r0n0c Oh. Thanks!

@benshemmeld
Copy link

This fix works nicely! Now I'm just having an issue on my Roku 3. Any chance we could get this fix merged into master?

@shoguevara
Copy link

Hi all! Thanks Pablo for your efforts! I really appreciate it!
Anyway, I had this plugin up and running till past week. I decided to enable 2way authentication with Google and In spite of generating and using app password the plugin stopped working and I got "Channel not responding" error. I disabled 2way authentication, but it didn't help.
Plugin log is here http://pastebin.com/nk2qWyRk
Running it on Win 10
//=========================
UPD: Sorry, guys. My bad! It appeared to be a local problem. Works fine with fresh windows install - had to test it on VM.

@samm-git
Copy link

I can confirm, it does not work on OSX with Crypto installed:

2016-01-18 10:54:59,967 (7000024af000) :  DEBUG (runtime:717) - Handling request GET /music/googlemusic
2016-01-18 10:54:59,968 (7000024af000) :  DEBUG (runtime:814) - Found route matching /music/googlemusic
2016-01-18 10:54:59,971 (7000024af000) :  CRITICAL (runtime:889) - Exception (most recent call last):
  File "bundles-release/Framework.bundle-dist-ninja/Contents/Resources/Versions/2/Python/Framework/components/runtime.py", line 843, in handle_request
  File "bundles-release/Framework.bundle-dist-ninja/Contents/Resources/Versions/2/Python/Framework/handlers/base.py", line 111, in call
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Code/__init__.py", line 47, in MainMenu
    API.authenticate(Prefs['email'], Prefs['password'])
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusic.py", line 56, in authenticate
    mcauthenticated = self._mobileclient.login(email, password, Mobileclient.FROM_MAC_ADDRESS)
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusicapi/clients/mobileclient.py", line 63, in login
    if not self.session.login(email, password, android_id):
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gmusicapi/session.py", line 163, in login
    res = gpsoauth.perform_master_login(email, password, android_id)
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gpsoauth/__init__.py", line 56, in perform_master_login
    'EncryptedPasswd': google.signature(email, password, android_key_7_3_29),
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/gpsoauth/google.py", line 51, in signature
    encrypted_login = cipher.encrypt((email + u'\x00' + password).encode('utf-8'))
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/Crypto/Cipher/PKCS1_OAEP.py", line 164, in encrypt
    m = self._key.encrypt(em, 0)[0]
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/Crypto/PublicKey/RSA.py", line 150, in encrypt
    return pubkey.pubkey.encrypt(self, plaintext, K)
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/Crypto/PublicKey/pubkey.py", line 75, in encrypt
    ciphertext=self._encrypt(plaintext, K)
  File "/Users/asamorukov/Library/Application Support/Plex Media Server/Plug-ins/GoogleMusic.bundle/Contents/Libraries/Shared/Crypto/PublicKey/RSA.py", line 224, in _encrypt
    return (self.key._encrypt(c),)
ValueError: Plaintext too large

Update: it works when pycrypto compiled for the py-27

@shoguevara
Copy link

For some reasons I started getting the error I mentioned above - #13 (comment)
Only fix I could find is using my actual device Android ID in gmusic.py file.
So, changing the line _mcauthenticated = self.mobileclient.login(email, password, Mobileclient.FROM_MAC_ADDRESS) to _mcauthenticated = self.mobileclient.login(email, password, "myadroiddeviceid") did the trick. Could anyone please look into it?

@samm-git
Copy link

I finally been able to get it running, thank you! When testing i found one very easy to fix error - for the artists in non-ASCII it wont work correctly. I patches this issue and now it works fine. Patch provided below:

diff --git a/Contents/Libraries/Shared/gmusic.py b/Contents/Libraries/Shared/gmusic.py
index 94417ad..7aa3b5a 100644
--- a/Contents/Libraries/Shared/gmusic.py
+++ b/Contents/Libraries/Shared/gmusic.py
@@ -133,13 +133,13 @@ class GMusic(object):
     def get_tracks_for_type(self, type, name):
         type = type.lower()
         if type == 'artists':
-            return self.tracks_by_artist[name]
+            return self.tracks_by_artist[name.decode('utf_8')]
         elif type == 'albums':
-            return self.tracks_by_album[name]
+            return self.tracks_by_album[name.decode('utf_8')]
         elif type == 'genres':
-            return self.tracks_by_genre[name]
+            return self.tracks_by_genre[name.decode('utf_8')]
         elif type == 'songs by letter':
-            return self.tracks_by_letter[name]
+            return self.tracks_by_letter[name.decode('utf_8')]
         else:
             return {}

@samm-git
Copy link

And one more unicode related fix: display title2 for non-ASCII songs/artists:

diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py
index 1a9b51b..a4f2998 100644
--- a/Contents/Code/__init__.py
+++ b/Contents/Code/__init__.py
@@ -288,7 +288,7 @@ def ShowSongs(title, shuffle=False, page=1):
 ################################################################################
 @route(PREFIX + '/getalbumsinlibrary')
 def GetAlbumsInLibrary(name):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))

     albums = API.get_albums_in_library(name)
     for album in sorted(albums['albums'], key = lambda x: x.get('year')):
@@ -307,7 +307,7 @@ def GetAlbumsInLibrary(name):
 ################################################################################
 @route(PREFIX + '/gettracklist')
 def GetTrackList(name, type):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))
     tracks = API.get_tracks_for_type(type, name)
     sort = 'title' if type == 'Songs By Letter' else 'trackType'
     for track in sorted(tracks, key = lambda x: x['track'].get(sort)):
@@ -318,7 +318,7 @@ def GetTrackList(name, type):
 ################################################################################
 @route(PREFIX + '/getplaylistcontents')
 def GetPlaylistContents(name, id):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))

     tracks = API.get_all_user_playlist_contents(id)
     for track in tracks:
@@ -334,7 +334,7 @@ def GetPlaylistContents(name, id):
 ################################################################################
 @route(PREFIX + '/getsharedplaylist')
 def GetSharedPlaylist(name, token):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))

     tracks = API.get_shared_playlist_contents(token)
     for track in tracks:
@@ -345,7 +345,7 @@ def GetSharedPlaylist(name, token):
 ################################################################################
 @route(PREFIX + '/getstationtracks')
 def GetStationTracks(name, id):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))

     tracks = API.get_station_tracks(id)
     for track in tracks:
@@ -363,7 +363,7 @@ def GetStationTracks(name, id):
 ################################################################################
 @route(PREFIX + '/genressubmenu', children=list)
 def GenresSubMenu(name, id, children=None):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))
     oc.add(DirectoryObject(key=Callback(CreateStation, id=id), title='Play ' + name))

     if children != None:
@@ -381,7 +381,7 @@ def CreateStation(id):
 ################################################################################
 @route(PREFIX + '/getartistinfo')
 def GetArtistInfo(name, id, inLibrary=False):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))

     artist = API.get_artist_info(id)
     for album in sorted(artist['albums'], key = lambda x: x.get('year')):
@@ -400,7 +400,7 @@ def GetArtistInfo(name, id, inLibrary=False):
 ################################################################################
 @route(PREFIX + '/getalbuminfo')
 def GetAlbumInfo(name, id):
-    oc = ObjectContainer(title2=name)
+    oc = ObjectContainer(title2=unicode(name))

     album = API.get_album_info(id)
     for track in album['tracks']:

@pablorusso
Copy link
Author

new version here => #20

@pablorusso pablorusso closed this Sep 13, 2016
@pablorusso pablorusso deleted the update_gmusicapi_fix_login branch September 13, 2016 04:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

Comments