From 23937e6cb8e349cdd2bac008e66f035c85f88031 Mon Sep 17 00:00:00 2001 From: Domenico Iezzi Date: Fri, 26 Jan 2018 18:24:19 +0100 Subject: [PATCH] Improved file delivery + minor changes * Updated user agent string with data taken from play-store-api * *download* and *delivery* functions will now return a python generator rather than raw bytes, to prevent app loading entire files in memory. This generator can be iterated to get chunk of bytes to write, as discussed in issue #35. * as a consequence of the previous point, there is no more progress bar feature for downloading. It should be implemented by developers using the API. --- gpapi/config.py | 21 +++++++++++++++------ gpapi/googleplay.py | 30 +++++++----------------------- setup.py | 1 - test.py | 12 +++++------- 4 files changed, 27 insertions(+), 37 deletions(-) diff --git a/gpapi/config.py b/gpapi/config.py index 1b18cd0..75ca751 100644 --- a/gpapi/config.py +++ b/gpapi/config.py @@ -77,12 +77,21 @@ class DeviceBuilder(object): ",device={device}" ",hardware={hardware}" ",product={product}" - "").format(versionString=version_string, - versionCode=self.device.get('vending.version'), - sdk=self.device.get('build.version.sdk_int'), - device=self.device.get('build.device'), - hardware=self.device.get('build.hardware'), - product=self.device.get('build.product')) + ",platformVersionRelease={platform_v}" + ",model={model}" + ",buildId={build_id}" + ",isWideScreen=0" + ",supportedAbis={supported_abis}" + ")").format(versionString=version_string, + versionCode=self.device.get('vending.version'), + sdk=self.device.get('build.version.sdk_int'), + device=self.device.get('build.device'), + hardware=self.device.get('build.hardware'), + product=self.device.get('build.product'), + platform_v=self.device.get('build.version.release'), + model=self.device.get('build.model'), + build_id=self.device.get('build.id'), + supported_abis=self.device.get('platforms')) def getAuthParams(self, email, passwd): return {"Email": email, diff --git a/gpapi/googleplay.py b/gpapi/googleplay.py index 3bb3211..19bfd1a 100644 --- a/gpapi/googleplay.py +++ b/gpapi/googleplay.py @@ -5,7 +5,6 @@ from Crypto.Util import asn1 from Crypto.PublicKey import RSA from Crypto.Hash import SHA from Crypto.Cipher import PKCS1_OAEP -from clint.textui import progress import requests from base64 import b64decode, urlsafe_b64encode @@ -478,30 +477,16 @@ class GooglePlayAPI(object): output.append(review) return output - def _deliver_data(self, url, cookies, progress_bar): + def _deliver_data(self, url, cookies): headers = self.getDefaultHeaders() - if not progress_bar: - return requests.get(url, headers=headers, - cookies=cookies, verify=ssl_verify, - stream=True, - timeout=60, - proxies=self.proxies_config).content - response_content = bytes() response = requests.get(url, headers=headers, cookies=cookies, verify=ssl_verify, stream=True, timeout=60, proxies=self.proxies_config) - total_length = int(response.headers.get('content-length')) - chunk_size = 32 * (1 << 10) # 32 KB - bar = progress.Bar(expected_size=(total_length >> 10)) - for index, chunk in enumerate(response.iter_content(chunk_size=chunk_size)): - response_content += chunk - bar.show(index * chunk_size >> 10) - bar.done() - return response_content + return response.iter_content(chunk_size=(32 * 1 << 10)) def delivery(self, packageName, versionCode=None, offerType=1, - downloadToken=None, progress_bar=False, expansion_files=False): + downloadToken=None, expansion_files=False): """Download an already purchased app. Args: @@ -553,7 +538,7 @@ class GooglePlayAPI(object): cookies = { str(cookie.name): str(cookie.value) } - result['data'] = self._deliver_data(downloadUrl, cookies, progress_bar) + result['data'] = self._deliver_data(downloadUrl, cookies) if not expansion_files: return result for obb in resObj.payload.deliveryResponse.appDeliveryData.additionalFile: @@ -566,12 +551,11 @@ class GooglePlayAPI(object): obbType = 'patch' a['type'] = obbType a['versionCode'] = obb.versionCode - a['data'] = self._deliver_data(obb.downloadUrl, None, progress_bar) + a['data'] = self._deliver_data(obb.downloadUrl, None) result['additionalData'].append(a) return result - def download(self, packageName, versionCode=None, offerType=1, - progress_bar=False, expansion_files=False): + def download(self, packageName, versionCode=None, offerType=1, expansion_files=False): """Download an app and return its raw data (APK file). Free apps need to be "purchased" first, in order to retrieve the download cookie. If you want to download an already purchased app, use *delivery* method. @@ -612,7 +596,7 @@ class GooglePlayAPI(object): else: dlToken = resObj.payload.buyResponse.downloadToken return self.delivery(packageName, versionCode, offerType, dlToken, - progress_bar=progress_bar, expansion_files=expansion_files) + expansion_files=expansion_files) @staticmethod def getDevicesCodenames(): diff --git a/setup.py b/setup.py index 79b5b40..2994dce 100644 --- a/setup.py +++ b/setup.py @@ -11,5 +11,4 @@ setup(name='gpapi', package_data={'gpapi': ['device.properties']}, install_requires=['pycryptodome', 'protobuf', - 'clint', 'requests']) diff --git a/test.py b/test.py index e8ba8d7..c847a72 100644 --- a/test.py +++ b/test.py @@ -37,9 +37,10 @@ for a in apps: docid = apps[0]['docId'] print('\nTelegram docid is: %s\n' % docid) print('\nAttempting to download %s\n' % docid) -fl = server.download(docid, None, progress_bar=True) -with open(docid + '.apk', 'wb') as f: - f.write(fl['data']) +fl = server.delivery(docid, versionCode=None) +with open(docid + '.apk', 'wb') as apk_file: + for chunk in fl.get('data'): + apk_file.write(chunk) print('\nDownload successful\n') # DOWNLOAD APP NOT PURCHASED @@ -52,10 +53,7 @@ try: app = server.search('nova launcher prime', 3, None) app = filter(lambda x: x['docId'] == 'com.teslacoilsw.launcher.prime', app) app = list(app)[0] - fl = server.delivery(app['docId'], app['versionCode'], progress_bar=True) - with open(docid + '.apk', 'wb') as f: - f.write(fl['data']) - print('\nDownload successful\n') + fl = server.delivery(app['docId'], app['versionCode']) except RequestError as e: errorThrown = True print(e)