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.
This commit is contained in:
Domenico Iezzi 2018-01-26 18:24:19 +01:00
parent e1c73c8af0
commit 23937e6cb8
No known key found for this signature in database
GPG Key ID: 7AC94D5DDA2FB7EE
4 changed files with 27 additions and 37 deletions

View File

@ -77,12 +77,21 @@ class DeviceBuilder(object):
",device={device}"
",hardware={hardware}"
",product={product}"
"").format(versionString=version_string,
",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'))
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,

View File

@ -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():

View File

@ -11,5 +11,4 @@ setup(name='gpapi',
package_data={'gpapi': ['device.properties']},
install_requires=['pycryptodome',
'protobuf',
'clint',
'requests'])

12
test.py
View File

@ -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)