示例#1
0
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'])
示例#2
0
server = GooglePlayAPI('en_EN', 'Usa/Chicago')

# LOGIN

print('\nLogging in with email and password\n')
server.login(args.email, args.password, None, None)
gsfId = server.gsfId
authSubToken = server.authSubToken

print('\nNow trying secondary login with ac2dm token and gsfId saved\n')
# server = GooglePlayAPI('it_IT', 'Europe/Rome')
server.login(None, None, gsfId, authSubToken)

# SEARCH

apps = server.search('telegram', 34, None)

print('\nSearch suggestion for "fir"\n')
print(server.searchSuggest('fir'))

print('nb_result: 34')
print('number of results: %d' % len(apps))

print('\nFound those apps:\n')
for a in apps:
    print(a['docId'])

# DOWNLOAD
docid = apps[0]['docId']
print('\nTelegram docid is: %s\n' % docid)
print('\nAttempting to download %s\n' % docid)
示例#3
0
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 = False
            self.verbose = False
            self.progress_bar = False
            self.logging_enable = False
            self.device_codename = 'bacon'
            self.addfiles_enable = False

        # if args are passed
        else:
            self.yes = args.yes_to_all
            self.verbose = args.verbose
            if self.verbose:
                logger.setLevel(logging.INFO)
                handler = logging.StreamHandler()
                formatter = logging.Formatter("[%(levelname)s] %(message)s")
                handler.setFormatter(formatter)
                logger.addHandler(handler)
                logger.propagate = False
            logger.info('GPlayCli version %s', __version__)
            logger.info('Configuration file is %s', credentials)
            self.progress_bar = args.progress_bar
            self.set_download_folder(args.update_folder)
            self.logging_enable = args.logging_enable
            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()

            if self.logging_enable:
                self.success_logfile = "apps_downloaded.log"
                self.failed_logfile = "apps_failed.log"
                self.unavail_logfile = "apps_not_available.log"

    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
            logger.error('cache file does not exists or is corrupted')
        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:
            err_str = "Failed to write token to cache file: %s %s" % (self.tokencachefile, e.strerror)
            logger.error(err_str)
            raise IOError(err_str)

    def retrieve_token(self, force_new=False):
        token, gsfid = self.get_cached_token()
        if token is not None and not force_new:
            logger.info("Using cached token.")
            return token, gsfid
        logger.info("Retrieving token ...")
        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(" ")
        logger.info("Token: %s", token)
        logger.info("GSFId: %s", gsfid)
        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:
            logger.info("Using credentials to connect to API")
            email = self.config["gmail_address"]
            if self.config["gmail_password"]:
                logger.info("Using plaintext 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:
            logger.info("Using token to connect to API")
            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, DecodeError) as ve:  # invalid token or expired
            logger.info("Token has expired or is invalid. Retrieving a new one...")
            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:
            logger.info("Checking 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)
            logger.info("Analyzing %s", filepath)
            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, filename, apk_version_code in zip(details, package_bunch, list_of_apks, 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':
                logger.info("Downloading ...")
                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

            logger.info("%s / %s %s", position, len(list_of_packages_to_download), packagename)

            # 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:
                logger.error("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:
                logger.error("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:
                    logger.error("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])

        if self.logging_enable:
            self.write_logfiles(success_items, failed_items, unavail_items)

        return_function(failed_downloads + unavail_downloads)
        return to_download_items - failed_items

    def after_download(self, 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:
            # skip that app if it not free
            # or if it's beta (pre-registration)
            if (len(result['offer']) == 0 # beta apps (pre-registration)
                or free_only
                and result['offer'][0]['checkoutFlowRequired'] # 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 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)
示例#4
0
server = GooglePlayAPI(debug=True)

# LOGIN

print('\nLogging in with email and password\n')
server.login(args.email, args.password, None, None)
gsfId = server.gsfId
authSubToken = server.authSubToken

print('\nNow trying secondary login with ac2dm token and gsfId saved\n')
server = GooglePlayAPI(debug=True)
server.login(None, None, gsfId, authSubToken)

# SEARCH

apps = server.search('telegram', 34, None)

print('nb_result: 34')
print('number of results: %d' % len(apps))

print('\nFound those apps:\n')
for a in apps:
    print(a['docId'])

# DOWNLOAD
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'])
示例#5
0
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'}
示例#6
0
class GPlaycli(object):
    def __init__(self, credentials=None, proxies=None, device='bacon',
                 locale=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]

        self.proxies = proxies

        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

        self.token_enable = True
        self.token_url = self.configparser.get('Credentials', 'token_url')
        self.token, self.gsfid = self.retrieve_token()

        # default settings, ie for API calls
        self.yes = False
        self.verbose = False
        logging.basicConfig()
        self.progress_bar = False
        self.logging_enable = False
        self.device_codename = device
        self.locale = locale
        self.addfiles_enable = False

    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 error:
            raise IOError("Failed to write token to cache file: %s %s" % (self.tokencachefile,
                                                                          error.strerror))

    def retrieve_token(self, force_new=False):
        token, gsfid = self.get_cached_token()
        if token is not None and not force_new:
            logging.info("Using cached token.")
            return token, gsfid
        logging.info("Retrieving token ...")
        resp = requests.get(self.token_url)
        if resp.text == 'Auth error':
            print('Token dispenser auth error, probably too many connections')
            sys.exit(ERRORS.TOKEN_DISPENSER_AUTH_ERROR)
        elif resp.text == "Server error":
            print('Token dispenser server error')
            sys.exit(ERRORS.TOKEN_DISPENSER_SERVER_ERROR)
        token, gsfid = resp.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):
        if self.locale:
            self.playstore_api = GooglePlayAPI(
                device_codename=self.device_codename,
                proxies_config=self.proxies,
                locale=self.locale,
                timezone=None
            )
        else:
            self.playstore_api = GooglePlayAPI(
                device_codename=self.device_codename,
                proxies_config=self.proxies,
                timezone=None
            )

        error = None
        email = None
        password = None
        auth_sub_token = None
        gsf_id = None
        if self.token_enable is False:
            logging.info("Using credentials to connect to API")
            email = self.config["gmail_address"]
            if self.config["gmail_password"]:
                logging.info("Using plaintext 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:
            logging.info("Using token to connect to API")
            auth_sub_token = self.token
            gsf_id = int(self.gsfid, 16)
        try:
            self.playstore_api.login(email=email,
                                     password=password,
                                     authSubToken=auth_sub_token,
                                     gsfId=gsf_id)
        except (ValueError, IndexError, LoginError, GoogleDecodeError):  # invalid token or expired
            logging.info("Token has expired or is invalid. Retrieving a new one...")
            self.retrieve_token(force_new=True)
            self.playstore_api.login(authSubToken=self.token, gsfId=int(self.gsfid, 16))
        success = True
        return success, error

    def download_pkg(self, pkg, version):
        # 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)

        #Download
        try:
            data_dict = self.playstore_api.download(pkg, version)
        except IndexError as exc:
            print("Error while downloading %s : %s" % (pkg,
                                                       "this package does not exist, "
                                                       "try to search it via --search before"))
            return False, None
        except LoginError:
            self.retrieve_token(force_new=True)
            self.playstore_api.login(authSubToken=self.token, gsfId=int(self.gsfid, 16))
            try:
                data_dict = self.playstore_api.download(pkg, version)
            except IndexError as exc:
                print("Error while downloading %s : %s" % (pkg,
                                                           "this package does not exist, "
                                                           "try to search it via --search before"))
                return False, None
            except Exception as exc:
                print("Error while downloading %s : %s" % (pkg, exc))
                return False, None
        except Exception as exc:
            print("Error while downloading %s : %s" % (pkg, exc))
            return False, None
        else:
            filename = pkg + ".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" % (pkg, exc))
        return True, filepath

    def raw_search(self, search_string, nb_results):
        # Query results
        return self.playstore_api.search(search_string, nb_result=nb_results)

    def search(self, search_string, nb_results=1, free_only=True):
        try:
            results = self.raw_search(search_string, nb_results)
        except IndexError:
            results = list()
        except LoginError:
            self.retrieve_token(force_new=True)
            self.playstore_api.login(authSubToken=self.token, gsfId=int(self.gsfid, 16))
            try:
                results = self.raw_search(search_string, nb_results)
            except IndexError:
                results = list()

        logging.info(results)
                
        if not results:
            print("No result")
            return
        all_results = list()
        # Compute results values
        for result in results:
            if free_only and result['offer'][0]['checkoutFlowRequired']:  # if not Free to download
                continue
            entry = {"title": result["title"],
                     "creator": result['author'],
                     "size": util.sizeof_fmt(result['installationSize']),
                     "downloads": result['numDownloads'],
                     "last_update": result['uploadDate'],
                     "app_id": result['docId'],
                     "version": result['versionCode'],
                     "rating": "%.2f" % result["aggregateRating"]["starRating"],
                     "paid": result['offer'][0]['checkoutFlowRequired'],
                     "stable": not result["unstable"]}
            all_results.append(entry)

        for result in all_results:
            if result["app_id"] == search_string:
                return result
        return "NOT_AT_PLAY"
示例#7
0
server = GooglePlayAPI('it_IT', 'Europe/Rome')

# LOGIN

print('\nLogging in with email and password\n')
server.login(args.email, args.password, None, None)
gsfId = server.gsfId
authSubToken = server.authSubToken

print('\nNow trying secondary login with ac2dm token and gsfId saved\n')
server = GooglePlayAPI('it_IT', 'Europe/Rome')
server.login(None, None, gsfId, authSubToken)

# SEARCH

apps = server.search('telegram', 34, None)

print('\nSearch suggestion for "fir"\n')
print(server.searchSuggest('fir'))

print('nb_result: 34')
print('number of results: %d' % len(apps))

print('\nFound those apps:\n')
for a in apps:
    print(a['docId'])

# HOME APPS

print('\nFetching apps from play store home\n')
home = server.getHomeApps()
示例#8
0
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'}
示例#9
0
class Play(object):
    def __init__(self, debug=True, fdroid=False):
        self.currentSet = []
        self.debug = debug
        self.fdroid = fdroid
        self.loggedIn = False
        self.firstRun = True

        # 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 ['/usr/bin', '/usr/local/bin']:
            exe = os.path.join(path, self.fdroid_exe)
            if os.path.isfile(exe):
                found = True
        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'], 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)
        # ensure all folder and files are setup
        p = Popen([self.fdroid_exe, 'update', '--create-key'],
                  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 fdroid_update(self):
        if not self.loggedIn:
            return makeError(NOT_LOGGED_IN_ERR)
        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')
                    return {'status': 'SUCCESS'}
            except:
                return makeError(FDROID_ERR)
        else:
            return {'status': 'SUCCESS'}

    def get_apps(self):
        if self.firstRun:
            return {'status': 'PENDING'}
        return {
            'status': 'SUCCESS',
            'message': sorted(self.currentSet, key=lambda k: k['title'])
        }

    def login(self, ciphertext, hashToB64):
        def unpad(s):
            return s[:-ord(s[len(s) - 1:])]

        try:
            cipher = base64.b64decode(ciphertext)
            passwd = base64.b64decode(hashToB64)
            # first 16 bytes corresponds to the init vector
            iv = cipher[0:16]
            cipher = cipher[16:]
            aes = AES.new(passwd, AES.MODE_CBC, iv)
            result = unpad(aes.decrypt(cipher)).split(b'\x00')
            email = result[0].decode('utf-8')
            password = result[1].decode('utf-8')
            self.service.login(email, password, None, None)
            self.loggedIn = True
            return {'status': 'SUCCESS', 'message': 'OK'}
        except LoginError as e:
            print('Wrong credentials')
            self.loggedIn = False
            return {'status': 'ERROR', 'message': 'Wrong credentials'}
        except RequestError as e:
            # probably tokens are invalid, so it is better to
            # invalidate them
            print('Request error, probably invalid token')
            self.loggedIn = False
            return {
                'status': 'ERROR',
                'message': 'Request error, probably invalid token'
            }

    def update_state(self):
        def get_details_from_apk(details):
            filepath = os.path.join(self.download_path,
                                    details['docId'] + '.apk')
            a = APK(filepath)
            details['versionCode'] = int(a.version_code)
            return details

        def fetch_details_for_local_apps():

            # get application ids from apk files
            appList = [
                os.path.splitext(apk)[0]
                for apk in os.listdir(self.download_path)
                if os.path.splitext(apk)[1] == '.apk'
            ]
            toReturn = []
            if len(appList) > 0:
                details = self.get_bulk_details(appList)
                executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)
                futures = {
                    executor.submit(get_details_from_apk, app): app
                    for app in details
                }
                for future in concurrent.futures.as_completed(futures):
                    app = future.result()
                    toReturn.append(app)
                    if self.debug:
                        print('Added %s to cache' % app['docId'])
            return toReturn

        print('Updating cache')
        self.currentSet = fetch_details_for_local_apps()
        self.firstRun = False

    def insert_app_into_state(self, newApp):
        found = False
        result = filter(lambda x: x['docId'] == newApp['docId'],
                        self.currentSet)
        result = list(result)
        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': 'ERROR', 'message': NOT_LOGGED_IN_ERR}

        try:
            apps = self.service.search(appName, numItems, None)
        except RequestError as e:
            print(SESSION_EXPIRED_ERR)
            self.loggedIn = False
            return {'status': 'ERROR', 'message': SESSION_EXPIRED_ERR}

        return {'status': 'SUCCESS', 'message': apps}

    def get_bulk_details(self, apksList):
        if not self.loggedIn:
            return {'status': 'ERROR', 'message': NOT_LOGGED_IN_ERR}
        try:
            apps = self.service.bulkDetails(apksList)
        except RequestError as e:
            print(SESSION_EXPIRED_ERR)
            self.loggedIn = False
            return {'status': 'ERROR', 'message': SESSION_EXPIRED_ERR}
        return apps

    def download_selection(self, appNames):
        if not self.loggedIn:
            return {'status': 'ERROR', 'error': NOT_LOGGED_IN_ERR}

        success = []
        failed = []
        unavail = []

        details = self.get_bulk_details(appNames)

        for appname, appdetails in zip(appNames, details):
            if appdetails['docId'] == '':
                print('Package does not exits')
                unavail.append(appname)
                continue
            print('Downloading %s' % appname)
            try:
                if appdetails['offer'][0]['formattedAmount'] == 'Free':
                    data = self.service.download(appname,
                                                 appdetails['versionCode'])
                else:
                    data = self.service.delivery(appname,
                                                 appdetails['versionCode'])
                success.append(appdetails)
                print('Done!')
            except IndexError as exc:
                print(exc)
                print('Package %s does not exists' % appname)
                unavail.append(appname)
            except Exception as exc:
                print(exc)
                print('Failed to download %s' % appname)
                failed.append(appname)
            else:
                filename = appname + '.apk'
                filepath = os.path.join(self.download_path, filename)
                try:
                    open(filepath, 'wb').write(data)
                except IOError as exc:
                    print('Error while writing %s: %s' % (filename, exc))
                    failed.append(appname)
        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': 'ERROR', 'error': NOT_LOGGED_IN_ERR}

        localDetails = self.currentSet
        onlineDetails = self.get_bulk_details(
            [app['docId'] for app in localDetails])
        if len(localDetails) == 0 or len(onlineDetails) == 0:
            print('There is no package locally')
            return {'status': 'SUCCESS', 'message': []}
        else:
            toUpdate = []
            for local, online in zip(localDetails, onlineDetails):
                if self.debug:
                    print('Checking %s' % local['docId'])
                    print('%d == %d ?' %
                          (local['versionCode'], online['versionCode']))
                if local['versionCode'] != online['versionCode']:
                    toUpdate.append(online['docId'])
        return {'status': 'SUCCESS', 'message': toUpdate}

    def remove_local_app(self, appName):
        apkName = appName + '.apk'
        apkPath = os.path.join(self.download_path, apkName)
        if os.path.isfile(apkPath):
            os.remove(apkPath)
            for pos, app in enumerate(self.currentSet):
                if app['docId'] == appName:
                    del self.currentSet[pos]
            return {'status': 'SUCCESS'}
        return {'status': 'ERROR'}
示例#10
0
import os

gsfId = int(os.environ["GPAPI_GSFID"])
authSubToken = os.environ["GPAPI_TOKEN"]

server = GooglePlayAPI("it_IT", "Europe/Rome")

# LOGIN
print("\nLogin with ac2dm token and gsfId saved\n")
server.login(None, None, gsfId, authSubToken)

# SEARCH
print("\nSearch suggestion for \"fir\"\n")
print(server.searchSuggest("fir"))

result = server.search("firefox")
for doc in result:
    if 'docid' in doc:
        print("doc: {}".format(doc["docid"]))
    for cluster in doc["child"]:
        print("\tcluster: {}".format(cluster["docid"]))
        for app in cluster["child"]:
            print("\t\tapp: {}".format(app["docid"]))

# HOME APPS
print("\nFetching apps from play store home\n")
result = server.home()
for cluster in result:
    print("cluster: {}".format(cluster.get("docid")))
    for app in cluster.get("child"):
        print("\tapp: {}".format(app.get("docid")))
示例#11
0
    print "Usage: %s request [nb_results] [offset]" % sys.argv[0]
    print "Search for an app."
    print "If request contains a space, don't forget to surround it with \"\""
    sys.exit(0)

request = sys.argv[1]
nb_res = 20
offset = None

if (len(sys.argv) >= 3):
    nb_res = int(sys.argv[2])

if (len(sys.argv) >= 4):
    offset = int(sys.argv[3])

server.login(GOOGLE_LOGIN, GOOGLE_PASSWORD, None, None)

apps = server.search(request, nb_res, offset)

# print('\nSearch suggestion for "fir"\n')
# print(server.searchSuggest('fir'))

print('nb_result: %d' % nb_res)
print('number of results: %d' % len(apps))

print('\nFound those apps:\n')
for a in apps:
    print("Title: " + a['title'] + ", PackageName: " + a['docId'] +
          ", Author: " + a['author'])

# {'files': [{'fileType': 0, 'version': 7, 'size': 23246144L}], 'recentChanges': u'', 'docId': u'com.unity.unitystagingproject', 'description': u'', 'title': u'UDPgame', 'author': u'Stone Shi', 'containsAds': u'', 'versionString': u'', 'aggregateRating': {'commentCount': 0L, 'fourStarRatings': 0L, 'oneStarRatings': 0L, 'twoStarRatings': 0L, 'fiveStarRatings': 0L, 'type': 2, 'starRating': 0.0, 'threeStarRatings': 0L, 'ratingsCount': 0L}, 'versionCode': 7, 'offer': [{'offerType': 1, 'micros': 0L, 'currencyCode': u'USD', 'formattedAmount': u'', 'saleEnds': u'', 'checkoutFlowRequired': False}], 'installationSize': 23246144L, 'unstable': True, 'uploadDate': u'24 lug 2018', 'dependencies': [], 'detailsUrl': u'details?doc=com.unity.unitystagingproject', 'images': [{'url': u'https://lh3.googleusercontent.com/IqqRcOiztNHJKnJlrie6b0x1cFY3otyT6aVrmsendF04tboly4BEIf8cUT8S8G1yBaQ', 'width': 512, 'supportsFifeUrlOptions': True, 'imageType': 4, 'height': 512}, {'url': u'https://lh3.googleusercontent.com/V1pJi6Qx5FbvjybKYnBXZQF_zbrNxksp_dld9GTLKJq5o6yNmkFKubWL20oiTrabOCo', 'width': 1024, 'supportsFifeUrlOptions': True, 'imageType': 2, 'height': 500}], 'permission': [], 'category': {'appCategory': u'GAME_ROLE_PLAYING', 'appType': u'GAME'}, 'numDownloads': u'Pi\xf9 di 0 di download'}