class ServerAPI(): def __init__(self, ConfigFile): self._config = ConfigFile self._server = GooglePlayAPI(self._config.get_locale(), self._config.get_timezone()) def login_with_token(self): self._server.login(None, None, self._config.get_gsfId(), self._config.get_authSubToken()) def get_token(self): self._server.login(self._config.get_mail(), self._config.get_password()) self._config.set_gsfId = self._server.gsfId self._config.set_authSubToken = self._server.authSubToken def download_app(self, appName): details = self._server.details(appName) docId = details.get('docId') filename = details.get('filename') if filename is None: filename = details.get('docId') + '.apk' if details is None: print('Package ', docid, ' does not exist on the server') else: print(docid, ' , ', filename, ' , ', details['versionCode']) data_gen = self._server.download(docid, details['versionCode']) data_gen = data_gen.get('file').get('data') filepath = filename with open(filepath, 'wb') as apk_file: for chunk in data_gen: apk_file.write(chunk)
def main(): # get args sys.argv.pop(0) appNames = sys.argv # read token cache tokenCache = configparser.ConfigParser() tokenCache.read('.cache.ini') try: gsfId = int(tokenCache['DEFAULT'] ['gsfId']) # does not get saved as int, but as str! authSubToken = tokenCache['DEFAULT']['authSubToken'] timezone = tokenCache['DEFAULT']['timezone'] locale = tokenCache['DEFAULT']['locale'] except configparser.NoSectionError: print( "Missing login data. Please, check your cache file, or run login.py" ) sys.exit(1) except configparser.ParsingError: print( "The cache file could not be read correctly. Please, check it, or run login.py" ) sys.exit(1) except configparser.Error as e: print("Error ", e, " while reading cache file") sys.exit(1) server = GooglePlayAPI(locale, timezone) # log in with saved credentials try: server.login(None, None, gsfId, authSubToken) except: print("Error while trying to login to GP servers") sys.exit(2) for app in appNames: try: details = server.details(app) docid = details.get('docId') filename = details.get('filename') if filename is None: filename = details.get('docId') + '.apk' if details is None: print('Package ', docid, ' does not exist') continue except: print("Error while trying to get details for", app) else: print(docid, ' , ', filename, details['versionCode']) data_gen = server.download(docid, details['versionCode']) data_gen = data_gen.get('file').get('data') filepath = filename with open(filepath, 'wb') as apk_file: for chunk in data_gen: apk_file.write(chunk)
def main(): # get args sys.argv.pop(0) searchQuery = ' '.join(sys.argv) # read token cache tokenCache = configparser.ConfigParser() tokenCache.read('.cache.ini') try: gsfId = int(tokenCache['DEFAULT'] ['gsfId']) # does not get saved as int, but as str! authSubToken = tokenCache['DEFAULT']['authSubToken'] timezone = tokenCache['DEFAULT']['timezone'] locale = tokenCache['DEFAULT']['locale'] except KeyError: print( "Missing login data. Please, check your cache file, or run login.py" ) sys.exit(1) except configparser.ParsingError: print( "The cache file could not be read correctly. Please, check it, or run login.py" ) sys.exit(1) except configparser.Error as e: print("Error ", e, " while reading cache file") sys.exit(1) server = GooglePlayAPI(locale, timezone) # log in with saved credentials try: server.login(None, None, gsfId, authSubToken) except: print("Error while trying to login to GP servers") sys.exit(2) resultRaw = server.search(searchQuery, 20) for app in resultRaw: docid = app.get('docId') details = server.details(docid) filename = app.get('filename') if filename is None: filename = details.get('docId') + '.apk' if details is None: print('Package ', docid, ' does not exist') continue print(docid, ' , ', filename, details['versionCode'])
# BULK DETAILS testApps = ['org.mozilla.firefox', 'com.non.existing.app'] bulk = server.bulkDetails(testApps) print('\nTesting behaviour for non-existing apps\n') if bulk[1] is not None: print('bulkDetails should return None for non-existing apps') sys.exit(1) print('\nResult from bulkDetails for %s\n' % testApps[0]) print(bulk[0]) # DETAILS print('\nGetting details for %s\n' % testApps[0]) details = server.details(testApps[0]) print(details) # REVIEWS print('\nGetting reviews for %s\n' % testApps[0]) revs = server.reviews(testApps[0]) for r in revs: print(r['comment']) # BROWSE print('\nBrowse play store categories\n') browse = server.browse() for b in browse: print(b['name'])
class Play(object): def __init__(self, debug=True, fdroid=False): self.currentSet = [] self.totalNumOfApps = 0 self.debug = debug self.fdroid = fdroid self.firstRun = True self.loggedIn = False self._email = None self._passwd = None self._gsfId = None self._token = None self._last_fdroid_update = None # configuring download folder if self.fdroid: self.download_path = os.path.join(os.getcwd(), 'repo') else: self.download_path = os.getcwd() # configuring fdroid data if self.fdroid: self.fdroid_exe = 'fdroid' self.fdroid_path = os.getcwd() self.fdroid_init() # language settings locale = os.environ.get('LANG_LOCALE') if locale is None: locale = locale_service.getdefaultlocale()[0] timezone = os.environ.get('LANG_TIMEZONE') if timezone is None: timezone = 'Europe/Berlin' device = os.environ.get('DEVICE_CODE') if device is None: self.service = GooglePlayAPI(locale, timezone) else: self.service = GooglePlayAPI(locale, timezone, device_codename=device) def fdroid_init(self): found = False for path in os.environ['PATH'].split(':'): exe = os.path.join(path, self.fdroid_exe) if os.path.isfile(exe): found = True break if not found: print('Please install fdroid') sys.exit(1) elif os.path.isfile('config.py'): print('Repo already initalized, skipping init') else: p = Popen([self.fdroid_exe, 'init', '-v'], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if p.returncode != 0: sys.stderr.write("error initializing fdroid repository " + stderr.decode('utf-8')) sys.exit(1) # backup config.py if self.debug: print('Checking config.py file') with open('config.py', 'r') as config_file: content = config_file.readlines() with open('config.py', 'w') as config_file: # copy all the original content of config.py # if the file was not modified with custom values, do it modified = False for line in content: if '# playmaker' in line: modified = True config_file.write(line) if not modified: if self.debug: print('Appending playmaker data to config.py') config_file.write( '\n# playmaker\nrepo_name = "playmaker"\n' 'repo_description = "repository managed with ' 'playmaker https://github.com/NoMore201/playmaker"\n') # ensure all folder and files are setup p = Popen([self.fdroid_exe, 'update', '--create-key', '-v'], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if p.returncode != 0: print('Skipping fdroid update') else: print('Fdroid repo initialized successfully') def get_last_fdroid_update(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} return {'status': 'SUCCESS', 'message': str(self._last_fdroid_update)} def fdroid_update(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} if self.fdroid: try: p = Popen([self.fdroid_exe, 'update', '-c', '--clean'], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if p.returncode != 0: sys.stderr.write("error updating fdroid repository " + stderr.decode('utf-8')) return makeError(FDROID_ERR) else: print('Fdroid repo updated successfully') self._last_fdroid_update = dt.today().replace( microsecond=0) return {'status': 'SUCCESS'} except Exception as e: return makeError(FDROID_ERR) else: return {'status': 'SUCCESS'} def get_apps(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} if self.firstRun: return { 'status': 'PENDING', 'total': self.totalNumOfApps, 'current': len(self.currentSet) } return { 'status': 'SUCCESS', 'message': sorted(self.currentSet, key=lambda k: k['title']) } def set_encoded_credentials(self, email, password): self._email = base64.b64decode(email).decode('utf-8') self._passwd = base64.b64decode(password).decode('utf-8') def set_credentials(self, email, password): self._email = email self._passwd = password def set_token_credentials(self, gsfId, token): self._gsfId = int(gsfId, 16) self._token = token def has_credentials(self): passwd_credentials = self._email is not None and self._passwd is not None token_credentials = self._gsfId is not None and self._token is not None return passwd_credentials or token_credentials def login(self): if self.loggedIn: return { 'status': 'SUCCESS', 'securityCheck': False, 'message': 'OK' } try: if not self.has_credentials(): raise LoginError("missing credentials") self.service.login(self._email, self._passwd, self._gsfId, self._token) self.loggedIn = True return { 'status': 'SUCCESS', 'securityCheck': False, 'message': 'OK' } except LoginError as e: print('LoginError: {0}'.format(e)) self.loggedIn = False return { 'status': 'ERROR', 'securityCheck': False, 'message': 'Wrong credentials' } except SecurityCheckError as e: print('SecurityCheckError: {0}'.format(e)) self.loggedIn = False return { 'status': 'ERROR', 'securityCheck': True, 'message': 'Need security check' } except RequestError as e: # probably tokens are invalid, so it is better to # invalidate them print('RequestError: {0}'.format(e)) self.loggedIn = False return { 'status': 'ERROR', 'securityCheck': False, 'message': 'Request error, probably invalid token' } def update_state(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} print('Updating cache') with concurrent.futures.ProcessPoolExecutor() as executor: # get application ids from apk files apkFiles = [ apk for apk in os.listdir(self.download_path) if os.path.splitext(apk)[1] == '.apk' ] self.totalNumOfApps = len(apkFiles) if self.totalNumOfApps != 0: future_to_app = [ executor.submit(get_details_from_apk, a, self.download_path, self.service) for a in apkFiles ] for future in concurrent.futures.as_completed(future_to_app): app = future.result() if app is not None: self.currentSet.append(app) print('Cache correctly initialized') self.firstRun = False def insert_app_into_state(self, newApp): found = False result = list( filter(lambda x: x['docid'] == newApp['docid'], self.currentSet)) if len(result) > 0: found = True if self.debug: print('%s is already cached, updating..' % newApp['docid']) i = self.currentSet.index(result[0]) self.currentSet[i] = newApp if not found: if self.debug: print('Adding %s into cache..' % newApp['docid']) self.currentSet.append(newApp) def search(self, appName, numItems=15): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} try: apps = self.service.search(appName) except RequestError as e: print(e) self.loggedIn = False return {'status': 'ERROR', 'message': SESSION_EXPIRED_ERR} except LoginError as e: print(SESSION_EXPIRED_ERR) self.loggedIn = False except IndexError as e: print(SESSION_EXPIRED_ERR) self.loggedIn = False return {'status': 'SUCCESS', 'message': apps} def details(self, app): try: details = self.service.details(app) except RequestError: details = None return details def get_bulk_details(self, apksList): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} try: apps = [self.details(a) for a in apksList] except LoginError as e: print(e) self.loggedIn = False return apps def download_selection(self, apps): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} success = [] failed = [] unavail = [] for app in apps: docid = app.get('docid') details = self.details(docid) filename = app.get('filename') if filename is None: filename = details.get('docid') + '.apk' if details is None: print('Package %s does not exits' % docid) unavail.append(docid) continue print('Downloading %s' % docid) try: if details.get('offer')[0].get('micros') == 0: data_gen = self.service.download( docid, details.get('details').get('appDetails') ['versionCode']) else: data_gen = self.service.delivery( docid, details.get('details').get('appDetails') ['versionCode']) data_gen = data_gen.get('file').get('data') except IndexError as exc: print(exc) print('Package %s does not exists' % docid) unavail.append(docid) except Exception as exc: print(exc) print('Failed to download %s' % docid) failed.append(docid) else: filepath = os.path.join(self.download_path, filename) try: with open(filepath, 'wb') as apk_file: for chunk in data_gen: apk_file.write(chunk) except IOError as exc: print('Error while writing %s: %s' % (filename, exc)) failed.append(docid) details['filename'] = filename success.append(details) for x in success: self.insert_app_into_state(x) return { 'status': 'SUCCESS', 'message': { 'success': success, 'failed': failed, 'unavail': unavail } } def check_local_apks(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} if len(self.currentSet) == 0: print('There is no package') return {'status': 'SUCCESS', 'message': []} else: toUpdate = [] for app in self.currentSet: details = self.details(app.get('docid')) #print(details) if details is None: print('%s not available in Play Store' % app['docid']) continue details['filename'] = app.get('filename') if self.debug: print('Checking %s' % app['docid']) print('%d == %d ?' % (app.get('details').get('appDetails')['versionCode'], details.get('details').get('appDetails') ['versionCode'])) if app.get('details').get( 'appDetails')['versionCode'] != details.get( 'details').get('appDetails')['versionCode']: toUpdate.append(details) return {'status': 'SUCCESS', 'message': toUpdate} def remove_local_app(self, docid): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} # get app from cache app = list(filter(lambda x: x['docid'] == docid, self.currentSet)) if len(app) < 1: return {'status': 'ERROR'} apkPath = os.path.join(self.download_path, app[0]['filename']) if os.path.isfile(apkPath): os.remove(apkPath) self.currentSet.remove(app[0]) return {'status': 'SUCCESS'} return {'status': 'ERROR'}
# BULK DETAILS testApps = ['org.mozilla.focus', 'com.non.existing.app'] bulk = server.bulkDetails(testApps) print('\nTesting behaviour for non-existing apps\n') if bulk[1] is not None: print('bulkDetails should return None for non-existing apps') sys.exit(1) print('\nResult from bulkDetails for %s\n' % testApps[0]) print(bulk[0]) # DETAILS print('\nGetting details for %s\n' % testApps[0]) details = server.details(testApps[0]) print(details) # REVIEWS print('\nGetting reviews for %s\n' % testApps[0]) revs = server.reviews(testApps[0]) for r in revs: print(r['comment']) # BROWSE print('\nBrowse play store categories\n') browse = server.browse() for b in browse: print(b['name'])
class Play(object): def __init__(self, debug=True, fdroid=False): self.currentSet = [] self.totalNumOfApps = 0 self.debug = debug self.fdroid = fdroid self.firstRun = True self.loggedIn = False self._email = None self._passwd = None self._last_fdroid_update = None # configuring download folder if self.fdroid: self.download_path = os.path.join(os.getcwd(), 'repo') else: self.download_path = os.getcwd() # configuring fdroid data if self.fdroid: self.fdroid_exe = 'fdroid' self.fdroid_path = os.getcwd() self.fdroid_init() self.service = GooglePlayAPI(self.debug) def fdroid_init(self): found = False for path in os.environ['PATH'].split(':'): exe = os.path.join(path, self.fdroid_exe) if os.path.isfile(exe): found = True break if not found: print('Please install fdroid') sys.exit(1) elif os.path.isfile('./config.py'): print('Repo already initalized, skipping') else: p = Popen([self.fdroid_exe, 'init', '-v'], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if p.returncode != 0: sys.stderr.write("error initializing fdroid repository " + stderr.decode('utf-8')) sys.exit(1) # backup config.py if self.debug: print('Backing up config.py') move('./config.py', './config-backup.py') with open('./config-backup.py') as f1: content = f1.readlines() # copy all content of backup in the main config.py # if the file was not modified with custom values, do it with open('./config.py', 'w') as f: modified = False for line in content: if '# playmaker' in line: modified = True f.write(line) if not modified: if self.debug: print('Appending playmaker data to config.py') f.write('\n# playmaker\nrepo_name = "playmaker"\n' 'repo_description = "repository managed with ' 'playmaker https://github.com/NoMore201/playmaker"\n') os.chmod('./config.py', 0o600) # ensure all folder and files are setup p = Popen([self.fdroid_exe, 'update', '--create-key', '-v'], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if p.returncode != 0: sys.stderr.write("error initializing fdroid repository " + stderr.decode('utf-8')) else: print('Fdroid repo initialized successfully') def get_last_fdroid_update(self): return {'status': 'SUCCESS', 'message': str(self._last_fdroid_update)} def fdroid_update(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} if self.fdroid: try: p = Popen([self.fdroid_exe, 'update', '-c', '--clean'], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if p.returncode != 0: sys.stderr.write("error updating fdroid repository " + stderr.decode('utf-8')) return makeError(FDROID_ERR) else: print('Fdroid repo updated successfully') self._last_fdroid_update = dt.today().replace( microsecond=0) return {'status': 'SUCCESS'} except Exception as e: return makeError(FDROID_ERR) else: return {'status': 'SUCCESS'} def get_apps(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} if self.firstRun: return { 'status': 'PENDING', 'total': self.totalNumOfApps, 'current': len(self.currentSet) } return { 'status': 'SUCCESS', 'message': sorted(self.currentSet, key=lambda k: k['title']) } def login(self, email=None, password=None): def unpad(s): return s[:-ord(s[len(s) - 1:])] try: if email is not None and password is not None: self._email = base64.b64decode(email).decode('utf-8') self._passwd = base64.b64decode(password).decode('utf-8') self.service.login(self._email, self._passwd, None, None) else: # otherwise we need only to refresh auth token encrypted = self.service.encrypt_password( self._email, self._passwd).decode('utf-8') self.service.getAuthSubToken(self._email, encrypted) self.loggedIn = True return {'status': 'SUCCESS', 'message': 'OK'} except LoginError as e: print('Wrong credentials: {0}'.format(e)) return {'status': 'ERROR', 'message': 'Wrong credentials'} except RequestError as e: # probably tokens are invalid, so it is better to # invalidate them print(e) return { 'status': 'ERROR', 'message': 'Request error, probably invalid token' } def update_state(self): print('Updating cache') with concurrent.futures.ProcessPoolExecutor() as executor: # get application ids from apk files apkFiles = [ apk for apk in os.listdir(self.download_path) if os.path.splitext(apk)[1] == '.apk' ] self.totalNumOfApps = len(apkFiles) if self.totalNumOfApps != 0: future_to_app = [ executor.submit(get_details_from_apk, a, self.download_path, self.service) for a in apkFiles ] for future in concurrent.futures.as_completed(future_to_app): app = future.result() if app is not None: self.currentSet.append(app) print('Cache correctly initialized') self.firstRun = False def insert_app_into_state(self, newApp): found = False result = list( filter(lambda x: x['docId'] == newApp['docId'], self.currentSet)) if len(result) > 0: found = True if self.debug: print('%s is already cached, updating..' % newApp['docId']) i = self.currentSet.index(result[0]) self.currentSet[i] = newApp if not found: if self.debug: print('Adding %s into cache..' % newApp['docId']) self.currentSet.append(newApp) def search(self, appName, numItems=15): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} try: apps = self.service.search(appName, numItems, None) except RequestError as e: print(e) self.loggedIn = False return {'status': 'ERROR', 'message': SESSION_EXPIRED_ERR} except LoginError as e: print(SESSION_EXPIRED_ERR) self.loggedIn = False except IndexError as e: print(SESSION_EXPIRED_ERR) self.loggedIn = False return {'status': 'SUCCESS', 'message': apps} def details(self, app): try: details = self.service.details(app) except RequestError: details = None return details def get_bulk_details(self, apksList): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} try: apps = [self.details(a) for a in apksList] except LoginError as e: print(e) self.loggedIn = False return apps def download_selection(self, appNames): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} success = [] failed = [] unavail = [] for app in appNames: details = self.details(app) if details is None: print('Package %s does not exits' % app) unavail.append(app) continue print('Downloading %s' % app) try: if details['offer'][0]['formattedAmount'] == 'Free': data = self.service.download(app, details['versionCode']) else: data = self.service.delivery(app, details['versionCode']) except IndexError as exc: print(exc) print('Package %s does not exists' % app) unavail.append(app) except Exception as exc: print(exc) print('Failed to download %s' % app) failed.append(app) else: filename = app + '.apk' filepath = os.path.join(self.download_path, filename) try: open(filepath, 'wb').write(data['data']) except IOError as exc: print('Error while writing %s: %s' % (filename, exc)) failed.append(app) details['filename'] = filename success.append(details) for x in success: self.insert_app_into_state(x) return { 'status': 'SUCCESS', 'message': { 'success': success, 'failed': failed, 'unavail': unavail } } def check_local_apks(self): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} if len(self.currentSet) == 0: print('There is no package') return {'status': 'SUCCESS', 'message': []} else: toUpdate = [] for app in self.currentSet: details = self.details(app['docId']) if details is None: print('%s not available in Play Store' % app['docId']) continue if self.debug: print('Checking %s' % app['docId']) print('%d == %d ?' % (app['versionCode'], details['versionCode'])) if app['versionCode'] != details['versionCode']: toUpdate.append(details['docId']) return {'status': 'SUCCESS', 'message': toUpdate} def remove_local_app(self, docId): if not self.loggedIn: return {'status': 'UNAUTHORIZED'} # get app from cache app = list(filter(lambda x: x['docId'] == docId, self.currentSet)) if len(app) < 1: return {'status': 'ERROR'} apkPath = os.path.join(self.download_path, app[0]['filename']) if os.path.isfile(apkPath): os.remove(apkPath) self.currentSet.remove(app[0]) return {'status': 'SUCCESS'} return {'status': 'ERROR'}
device_codename=args.devicecode) server.login(email, password, None, None) gsfId = str(server.gsfId) authSubToken = server.authSubToken if not config.has_section(CONFIG_SECTION): config.add_section(CONFIG_SECTION) # write fetched values to config config.set(CONFIG_SECTION, CONFIG_GSFID, gsfId) config.set(CONFIG_SECTION, CONFIG_AUTHSUBTOKEN, str(authSubToken)) config.write(configfile) server = GooglePlayAPI(SERVER_LOCALE, SERVER_TZ, device_codename=args.devicecode) server.login(None, None, int(gsfId), authSubToken) app = server.details(args.package) docid = app['docId'] ver = app["versionString"] filename = docid + "-" + ver + ".apk" print("Fetching docId", docid, "to file", filename) #print(app) fl = server.download(docid) with open(filename, 'wb') as apk_file: for chunk in fl.get('file').get('data'): apk_file.write(chunk) print('\nDownload successful\n')
device_log_ins = json.load(logins) current_log_in = device_log_ins['pixel_2'] # Change this to change device server = GooglePlayAPI("en_US", "America/Toronto", current_log_in['deviceName']) print("Logging in...") server.login(gsfId=current_log_in['gsfId'], authSubToken=current_log_in['authSubToken']) print("Complete!") # Replace these with your package and version code names docid = "com.package.name" versionCode = 1234 details = server.details(docid, versionCode) title = details['title'].replace("\n", "-") print(f"""App Details Below: Name: {title} Package: {details['docid']} Version Code: {details['details']['appDetails']['versionCode']} App Version: {details['details']['appDetails']['versionString']} """) print("Attempting to download {}".format(docid)) fl = server.download(docid, versionCode=versionCode) with open(f"{docid}.apk", "wb") as apk_file: parts = int(fl['file']['total_size']) / fl['file']['chunk_size'] for index, chunk in enumerate(fl.get("file").get("data")):
class GPlaycli(object): def __init__(self, args=None, credentials=None): # no config file given, look for one if credentials is None: # default local user configs cred_paths_list = [ 'gplaycli.conf', os.path.expanduser("~") + '/.config/gplaycli/gplaycli.conf', '/etc/gplaycli/gplaycli.conf' ] tmp_list = list(cred_paths_list) while not os.path.isfile(tmp_list[0]): tmp_list.pop(0) if not tmp_list: raise OSError("No configuration file found at %s" % cred_paths_list) credentials = tmp_list[0] default_values = dict() self.configparser = configparser.ConfigParser(default_values) self.configparser.read(credentials) self.config = {key: value for key, value in self.configparser.items("Credentials")} self.tokencachefile = os.path.expanduser(self.configparser.get("Cache", "token")) self.playstore_api = None # default settings, ie for API calls if args is None: self.yes = True self.verbose = True self.progress_bar = True self.device_codename = 'bacon' self.addfiles_enable = False self.token_enable = self.configparser.getboolean('Credentials', 'token') self.token_url = self.configparser.get('Credentials', 'token_url') self.token, self.gsfid = self.retrieve_token() # if args are passed else: self.yes = args.yes_to_all self.verbose = args.verbose self.progress_bar = args.progress_bar self.set_download_folder(args.update_folder) self.device_codename = args.device_codename self.addfiles_enable = args.addfiles_enable if args.token_enable is None: self.token_enable = self.configparser.getboolean('Credentials', 'token') else: self.token_enable = args.token_enable if self.token_enable: if args.token_url is None: self.token_url = self.configparser.get('Credentials', 'token_url') else: self.token_url = args.token_url self.token, self.gsfid = self.retrieve_token() def get_cached_token(self): try: with open(self.tokencachefile, 'r') as tcf: token, gsfid = tcf.readline().split() if not token: token = None gsfid = None except (IOError, ValueError): # cache file does not exists or is corrupted token = None gsfid = None return token, gsfid def write_cached_token(self, token, gsfid): try: # creates cachedir if not exists cachedir = os.path.dirname(self.tokencachefile) if not os.path.exists(cachedir): os.mkdir(cachedir) with open(self.tokencachefile, 'w') as tcf: tcf.write("%s %s" % (token, gsfid)) except IOError as e: raise IOError("Failed to write token to cache file: %s %s" % (self.tokencachefile, e.strerror)) def retrieve_token(self, force_new=False): token, gsfid = self.get_cached_token() if token is not None and not force_new: return token, gsfid r = requests.get(self.token_url) if r.text == 'Auth error': print('Token dispenser auth error, probably too many connections') sys.exit(ERRORS.TOKEN_DISPENSER_AUTH_ERROR) elif r.text == "Server error": print('Token dispenser server error') sys.exit(ERRORS.TOKEN_DISPENSER_SERVER_ERROR) token, gsfid = r.text.split(" ") self.token = token self.gsfid = gsfid self.write_cached_token(token, gsfid) return token, gsfid def set_download_folder(self, folder): self.config["download_folder_path"] = folder def connect_to_googleplay_api(self): self.playstore_api = GooglePlayAPI(device_codename=self.device_codename) error = None email = None password = None authSubToken = None gsfId = None if self.token_enable is False: email = self.config["gmail_address"] if self.config["gmail_password"]: password = self.config["gmail_password"] elif self.config["keyring_service"] and HAVE_KEYRING is True: password = keyring.get_password(self.config["keyring_service"], email) elif self.config["keyring_service"] and HAVE_KEYRING is False: print("You asked for keyring service but keyring package is not installed") sys.exit(ERRORS.KEYRING_NOT_INSTALLED) else: authSubToken = self.token gsfId = int(self.gsfid, 16) try: self.playstore_api.login(email=email, password=password, authSubToken=authSubToken, gsfId=gsfId) except (ValueError, IndexError, LoginError) as ve: # invalid token or expired self.retrieve_token(force_new=True) self.playstore_api.login(authSubToken=self.token, gsfId=int(self.gsfid, 16)) success = True return success, error def list_folder_apks(self, folder): list_of_apks = [filename for filename in os.listdir(folder) if filename.endswith(".apk")] return list_of_apks def prepare_analyse_apks(self): download_folder_path = self.config["download_folder_path"] list_of_apks = [filename for filename in os.listdir(download_folder_path) if os.path.splitext(filename)[1] == ".apk"] if list_of_apks: self.analyse_local_apks(list_of_apks, self.playstore_api, download_folder_path, self.prepare_download_updates) def analyse_local_apks(self, list_of_apks, playstore_api, download_folder_path, return_function): list_apks_to_update = [] package_bunch = [] version_codes = [] for position, filename in enumerate(list_of_apks): filepath = os.path.join(download_folder_path, filename) a = APK(filepath) packagename = a.package package_bunch.append(packagename) version_codes.append(a.version_code) # BulkDetails requires only one HTTP request # Get APK info from store details = playstore_api.bulkDetails(package_bunch) for detail, packagename, apk_version_code in zip(details, package_bunch, version_codes): store_version_code = detail['versionCode'] # Compare if apk_version_code != "" and int(apk_version_code) < int(store_version_code) and int( store_version_code) != 0: # Add to the download list list_apks_to_update.append([packagename, filename, int(apk_version_code), int(store_version_code)]) return_function(list_apks_to_update) def prepare_download_updates(self, list_apks_to_update): if list_apks_to_update: list_of_packages_to_download = [] # Ask confirmation before downloading message = "The following applications will be updated :" for packagename, filename, apk_version_code, store_version_code in list_apks_to_update: message += "\n%s Version : %s -> %s" % (filename, apk_version_code, store_version_code) list_of_packages_to_download.append([packagename, filename]) message += "\n" print(message) if not self.yes: print("\nDo you agree?") return_value = input('y/n ?') if self.yes or return_value == 'y': downloaded_packages = self.download_selection(self.playstore_api, list_of_packages_to_download, self.after_download) return_string = str() for package in downloaded_packages: return_string += package + " " print("Updated: " + return_string[:-1]) else: print("Everything is up to date !") sys.exit(ERRORS.OK) def download_selection(self, playstore_api, list_of_packages_to_download, return_function): success_downloads = list() failed_downloads = list() unavail_downloads = list() # BulkDetails requires only one HTTP request # Get APK info from store details = playstore_api.bulkDetails([pkg[0] for pkg in list_of_packages_to_download]) position = 1 for detail, item in zip(details, list_of_packages_to_download): packagename, filename = item # Check for download folder download_folder_path = self.config["download_folder_path"] if not os.path.isdir(download_folder_path): os.mkdir(download_folder_path) # Get the version code and the offer type from the app details # m = playstore_api.details(packagename) vc = detail['versionCode'] # Download try: data_dict = playstore_api.download(packagename, vc, progress_bar=self.progress_bar, expansion_files=self.addfiles_enable) success_downloads.append(packagename) except IndexError as exc: print("Error while downloading %s : %s" % (packagename, "this package does not exist, " "try to search it via --search before")) unavail_downloads.append((item, exc)) except Exception as exc: print("Error while downloading %s : %s" % (packagename, exc)) failed_downloads.append((item, exc)) else: if filename is None: filename = packagename + ".apk" filepath = os.path.join(download_folder_path, filename) data = data_dict['data'] additional_data = data_dict['additionalData'] try: open(filepath, "wb").write(data) if additional_data: for obb_file in additional_data: obb_filename = "%s.%s.%s.obb" % (obb_file["type"], obb_file["versionCode"], data_dict["docId"]) obb_filename = os.path.join(download_folder_path, obb_filename) open(obb_filename, "wb").write(obb_file["data"]) except IOError as exc: print("Error while writing %s : %s" % (packagename, exc)) failed_downloads.append((item, exc)) position += 1 success_items = set(success_downloads) failed_items = set([item[0] for item, error in failed_downloads]) unavail_items = set([item[0] for item, error in unavail_downloads]) to_download_items = set([item[0] for item in list_of_packages_to_download]) return_function(failed_downloads + unavail_downloads) return to_download_items - failed_items @staticmethod def after_download(failed_downloads): # Info message if not failed_downloads: message = "Download complete" else: message = "A few packages could not be downloaded :" for item, exception in failed_downloads: package_name, filename = item if filename is not None: message += "\n%s : %s" % (filename, package_name) else: message += "\n%s" % package_name message += "\n%s\n" % exception print(message) def raw_search(self, results_list, search_string, nb_results): # Query results return self.playstore_api.search(search_string, nb_result=nb_results) def search(self, results_list, search_string, nb_results, free_only=True, include_headers=True): try: results = self.raw_search(results_list, search_string, nb_results) except IndexError: results = list() if not results: print("No result") return all_results = list() if include_headers: # Name of the columns col_names = ["Title", "Creator", "Size", "Downloads", "Last Update", "AppID", "Version", "Rating"] all_results.append(col_names) # Compute results values for result in results: if free_only and result['offer'][0]['checkoutFlowRequired']: # if not Free to download continue l = [result['title'], result['author'], util.sizeof_fmt(result['installationSize']), result['numDownloads'], result['uploadDate'], result['docId'], result['versionCode'], "%.2f" % result["aggregateRating"]["starRating"] ] if len(all_results) < int(nb_results) + 1: all_results.append(l) if self.verbose: # Print a nice table col_width = list() for column_indice in range(len(all_results[0])): col_length = max([len("%s" % row[column_indice]) for row in all_results]) col_width.append(col_length + 2) for result in all_results: print("".join(str("%s" % item).strip().ljust(col_width[indice]) for indice, item in enumerate(result))) return all_results def download_packages(self, list_of_packages_to_download): self.download_selection(self.playstore_api, [(pkg, None) for pkg in list_of_packages_to_download], self.after_download) def get_package_info(self, package): return self.playstore_api.details(package) def write_logfiles(self, success, failed, unavail): for result, logfile in [(success, self.success_logfile), (failed, self.failed_logfile), (unavail, self.unavail_logfile) ]: if result: with open(logfile, 'w') as _buffer: for package in result: print(package, file=_buffer)
db="playstore_apps_db") mycursor = con.cursor() mycursor.execute(""" SELECT app_url FROM apps_urls WHERE id=35 """) res = mycursor.fetchall() for row in res: # print(type(row)) mypackageName = ''.join(row) print(mypackageName) details = server.details(mypackageName) app_playstore_id = details['docid'] app_playstore_id = ''.join(app_playstore_id) print("PlayStoreID: ", app_playstore_id) print(" ") App_name = details['title'] App_name = ''.join(App_name) print("Application Name : ", App_name) print(" ") if 'creator' in details: developer_name = details['creator'] developer_name = ''.join(developer_name) print("Developer Name : ", developer_name)
from gpapi.googleplay import GooglePlayAPI, RequestError import json server = GooglePlayAPI(debug=True, locale='id-ID') server.login( None, None, int('3d9dadcd34b1d5bf', 16), 'vAVzTIwkcGaYMqtXiOLtk-ty7Jwj5IltXs4o23oPDO3g5GPidel9ktzaVwPnZnQVX-n3eA.') details = server.details('com.snapchat.android') print(json.dumps(details, sort_keys=True, indent=4))