70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
import os
|
|
import re
|
|
|
|
from glob import glob, escape
|
|
from contextlib import contextmanager
|
|
from subprocess import check_call
|
|
from urllib.parse import urlparse, parse_qs
|
|
|
|
from gplaycli.gplaycli import GPlaycli
|
|
|
|
class BadPackageNameException(Exception):
|
|
pass
|
|
|
|
class dotdict(dict):
|
|
"""dot.notation access to dictionary attributes"""
|
|
__getattr__ = dict.get
|
|
__setattr__ = dict.__setitem__
|
|
__delattr__ = dict.__delitem__
|
|
|
|
def validate_package_name(package_name):
|
|
# https://developer.android.com/studio/build/application-id
|
|
"""
|
|
- It must have at least two segments (one or more dots).
|
|
- Each segment must start with a letter.
|
|
- All characters must be alphanumeric or an underscore [a-zA-Z0-9_].
|
|
"""
|
|
reg = r"^(?:[a-zA-Z]+(?:\d*[a-zA-Z_]*)*)(?:\.[a-zA-Z]+(?:\d*[a-zA-Z_]*)*)+$"
|
|
return re.match(reg, package_name) is not None
|
|
|
|
class APK:
|
|
def __init__(self, apk, conf={}, conf_file=None):
|
|
self.conf = conf
|
|
self.conf_file = conf_file
|
|
self.package_name = ''
|
|
if 'play.google.com/store/apps/details' in apk:
|
|
url = urlparse(apk)
|
|
if url.netloc == 'play.google.com':
|
|
self.package_name = parse_qs(url.query)['id'][0]
|
|
else:
|
|
self.package_name = apk
|
|
|
|
def download(self):
|
|
if not validate_package_name(self.package_name):
|
|
raise BadPackageNameException(f'error downloading {self.package_name}')
|
|
|
|
cli = GPlaycli(args=dotdict(self.conf), config_file=self.conf_file)
|
|
cli.download_folder = 'out'
|
|
d = cli.download([self.package_name]) # returns the number of downloaded packages
|
|
if len(d) == 0:
|
|
raise BadPackageNameException(f'error downloading {self.package_name}')
|
|
|
|
fname = os.path.join('out', self.package_name + '.apk')
|
|
if os.path.isfile(fname):
|
|
self.file_name = fname
|
|
|
|
def check_dimension(self, split=True, remove=True):
|
|
if split and os.path.getsize(self.file_name) > 50 * 1024 * 1023:
|
|
check_call(['split', '-b', '49M', self.file_name, self.file_name])
|
|
if remove:
|
|
os.remove(self.file_name)
|
|
return glob(escape(self.file_name) + '*')
|
|
|
|
@contextmanager
|
|
def send(self, split=True, remove=True):
|
|
files = self.check_dimension(split) # split if size >= 50MB
|
|
yield files
|
|
for f in files: # removing old files
|
|
if remove:
|
|
os.remove(f)
|