Initial
This commit is contained in:
45
src_mac/cfg.py
Executable file
45
src_mac/cfg.py
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
from configparser import ConfigParser
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import os
|
||||
|
||||
os.chdir(Path(__file__).parent.parent)
|
||||
|
||||
logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
|
||||
level=logging.INFO)
|
||||
Log = logging.getLogger('main')
|
||||
|
||||
|
||||
class Cfg():
|
||||
def __init__(self) -> None:
|
||||
cfg = ConfigParser()
|
||||
cfg.read('config.ini')
|
||||
# [main]
|
||||
self.itunes_server = cfg.get('main', 'itunes_server')
|
||||
self.win_server = cfg.get('main', 'win_server')
|
||||
self.ssh_cmd_crack = cfg.get('main', 'ssh_cmd_crack')
|
||||
self.ssh_cmd_sync = cfg.get('main', 'ssh_cmd_sync')
|
||||
self.max_os = cfg.get('main', 'max_os')
|
||||
# [zip]
|
||||
self.convert_plist = cfg.getboolean('zip', 'convert_binary_plist')
|
||||
# [paths]
|
||||
self.sync_in = Path(cfg.get('paths', 'sync_in'))
|
||||
self.sync_out = Path(cfg.get('paths', 'sync_out'))
|
||||
self.completed = Path(cfg.get('paths', 'complete'))
|
||||
self.download_fix = Path(cfg.get('paths', 'download_fix'))
|
||||
self.download_tmp = Path(cfg.get('paths', 'download_tmp'))
|
||||
# config validation
|
||||
for path in [self.sync_in, self.sync_out, self.completed]:
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f'Directory "{path}" does not exist.')
|
||||
# create dirs
|
||||
self.download_fix.mkdir(parents=True, exist_ok=True)
|
||||
self.download_tmp.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.__dict__)
|
||||
|
||||
|
||||
CONFIG = Cfg()
|
||||
# print(CONFIG)
|
||||
36
src_mac/crack.py
Executable file
36
src_mac/crack.py
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
from subprocess import Popen
|
||||
from time import sleep
|
||||
import os
|
||||
|
||||
from cfg import CONFIG, Log
|
||||
from move_em import moveEmAll
|
||||
from server import WinServer
|
||||
|
||||
|
||||
print('Start cracking ...')
|
||||
while True:
|
||||
file = next(CONFIG.sync_out.glob('*.ipa'), None)
|
||||
|
||||
if not file:
|
||||
print('Nothing to do. Retry in 10s ...')
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
Log.info('[install] %s ...', file.name)
|
||||
WinServer.install(file)
|
||||
|
||||
Log.info('[crack] %s ...', file.name)
|
||||
shell = Popen(CONFIG.ssh_cmd_crack, shell=True)
|
||||
if shell.wait() != 0:
|
||||
raise RuntimeError('Error during cracking command')
|
||||
|
||||
Log.info('[pull] %s ...', file.name)
|
||||
shell = Popen(CONFIG.ssh_cmd_sync, shell=True)
|
||||
if shell.wait() != 0:
|
||||
raise RuntimeError('Error during sync-download')
|
||||
|
||||
Log.info('[delete] %s', file.name)
|
||||
os.remove(file) # file on sync_out dir
|
||||
|
||||
moveEmAll()
|
||||
21
src_mac/download.py
Executable file
21
src_mac/download.py
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
from cfg import CONFIG, Log
|
||||
from lib import downloadAllUntil, enumAppIds
|
||||
from repack_ipa import repackIpa
|
||||
|
||||
|
||||
# findLatestVersion(000, CONFIG.max_os)
|
||||
# downloadSpecificVersion(000, 000)
|
||||
# exit(0)
|
||||
|
||||
appIds = list(enumAppIds())
|
||||
# appIds = [000]
|
||||
|
||||
for i, appId in enumerate(appIds):
|
||||
Log.info('Checking [%d/%d] %s', i + 1, len(appIds), appId)
|
||||
for i in range(150):
|
||||
path = downloadAllUntil(i, appId, CONFIG.max_os, rmIncompatible=True)
|
||||
if path:
|
||||
repackIpa(path)
|
||||
|
||||
print('done.')
|
||||
20
src_mac/extract_versions.py
Executable file
20
src_mac/extract_versions.py
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
from argparse import ArgumentParser
|
||||
import shutil
|
||||
|
||||
from cfg import CONFIG
|
||||
from lib import downloadPath, updateVersionMap, versionToInt
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli = ArgumentParser()
|
||||
cli.add_argument('ipa', nargs='+')
|
||||
cli.add_argument('-m', '--move', action='store_true')
|
||||
args = cli.parse_args()
|
||||
max_os = versionToInt(CONFIG.max_os)
|
||||
|
||||
for fname in args.ipa:
|
||||
info = updateVersionMap(fname)
|
||||
if args.move and versionToInt(info.osVer) <= max_os:
|
||||
download_path = downloadPath(info.appId, info.verId)
|
||||
shutil.move(fname, download_path)
|
||||
4
src_mac/ipatool-py/.gitignore
vendored
Executable file
4
src_mac/ipatool-py/.gitignore
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
.idea
|
||||
__pycache__
|
||||
venv/
|
||||
downloaded/
|
||||
112
src_mac/ipatool-py/README.md
Executable file
112
src_mac/ipatool-py/README.md
Executable file
@@ -0,0 +1,112 @@
|
||||
# IPATool-py
|
||||
|
||||
Python version of IPATool!
|
||||
|
||||
**Now Supports**:
|
||||
- **Purchasing App via `--purchase`**
|
||||
- **Downloading Old IPA via iTunes Server**
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
There're three commands: lookup, historyver, download. Each command's usage can be found by `-h` option.
|
||||
|
||||
You can also chain multiple command, last command's output will be passed to next command (so you don't need to supply some arguments like appVerId)
|
||||
|
||||
### Download Newest Version
|
||||
|
||||
Download an app using bundleID (-o can be omited)
|
||||
```
|
||||
python3 main.py lookup -b com.touchingapp.potatsolite -c JP download -e APPLE_EMAIL -p APPLE_PWD -o DIR
|
||||
```
|
||||
|
||||
Or appID (`XXXXX` in the `apps.apple.com/app/xxxx/idXXXXXX`)
|
||||
```
|
||||
python3 main.py download -i XXXXX
|
||||
```
|
||||
|
||||
You can also purchase apps when downloading using `--purchase`:
|
||||
```
|
||||
python3 main.py lookup -b com.touchingapp.potatsolite -c JP download --purchase -e APPLE_EMAIL -p APPLE_PWD -o DIR
|
||||
```
|
||||
|
||||
### Download OLD Version
|
||||
|
||||
Old versions must be downloaded together with `iTunes Server`. You can get `iTunes Server` in several ways:
|
||||
- Using [action-ipadown](https://github.com/NyaMisty/action-ipadown) directly, which integrated this tool
|
||||
- NOTE: this method does not support 2FA
|
||||
- Manually way with Windows: (supports 2FA)
|
||||
- download this repo: https://github.com/NyaMisty/actions-iTunes-header
|
||||
- install iTunes 12.6.5.3, from https://secure-appldnld.apple.com/itunes12/091-87819-20180912-69177170-B085-11E8-B6AB-C1D03409AD2A6/iTunes64Setup.exe
|
||||
- patch the iTunes using `actions-iTunes-header/workflow_helper/iTunesInstall/patch_itunes.py`
|
||||
- install frida: `pip3 install frida`
|
||||
- open iTunes, sign out & re-login your account
|
||||
- run: `actions-iTunes-header/workflow_helper/iTunesDownload/get_header.py`
|
||||
- Manually way with jailbroken iOS device: (supports 2FA)
|
||||
- download [KbsyncTool](https://github.com/Lessica/KbsyncTool/releases)
|
||||
- install `com.darwindev.kbsync_XXX.deb` on your jailbroken device
|
||||
- exec `kbsynctool -s 9000` on your jailbroken device
|
||||
- you will find log `Using -s http://192.168.100.227:9000/...`, use it as server address in next step
|
||||
|
||||
After setting up the server, you can run this tool to download a specific version
|
||||
```
|
||||
python3 main.py lookup -b com.touchingapp.potatsolite -c JP download -s http://127.0.0.1:9000 --appVerId 833889087
|
||||
```
|
||||
|
||||
NOTE: Some users are reporting that you need to **authorize computer** and make first purchase in iTunes with marked "do not ask for password" before using the iTunes server. (See #26)
|
||||
|
||||
### Get History Ver (usually used together with JSON)
|
||||
|
||||
Get all appVerId of app from Apple
|
||||
```
|
||||
python3 main.py lookup -b com.touchingapp.potatsolite -c JP historyver -e APPLE_EMAIL -p APPLE_PWD
|
||||
```
|
||||
|
||||
### Lookup (usually used together with JSON)
|
||||
|
||||
Query app basic information:
|
||||
- By bundleID: `python3 main.py lookup -b com.touchingapp.potatsolite -c JP`
|
||||
- By appID: `python3 main.py lookup -i 1239860606 -c JP`
|
||||
|
||||
Query appVerId:
|
||||
```
|
||||
python3 main.py lookup -b com.touchingapp.potatsolite -c JP --get-verid
|
||||
```
|
||||
|
||||
### Headless Usage
|
||||
|
||||
For each command you can use `--json` switch to get result of command in JSON
|
||||
|
||||
```
|
||||
python3 main.py --json lookup -b com.touchingapp.potatsolite -c JP --get-verid
|
||||
python3 main.py --json lookup -b com.touchingapp.potatsolite -c JP historyver -e APPLE_EMAIL -p APPLE_PWD
|
||||
```
|
||||
|
||||
### For Large Scale Scraping
|
||||
|
||||
You can download all versions of an app like this:
|
||||
```
|
||||
python3 main.py --json download --itunes-server http://XXX.XXX.XXX.XXX:9000 --appId 414478124 --purchase --downloadAllVersion
|
||||
```
|
||||
|
||||
- In this mode, errors will only be logged instead of interrupting the whole process
|
||||
- For each downloaded app version, it will output a line of json in stdout like this:
|
||||
```
|
||||
{"appName": "WeChat", "appBundleId": "com.tencent.xin", "appVer": "6.5.13.34", "appId": 414478124, "appVerId": 822899148, "downloadedIPA": "wechat\\com.tencent.xin-6.5.13.34-414478124-822899148.ipa", "downloadedVerId": 822899148}
|
||||
```
|
||||
Logs will only be printed to stderr, so you can parse this line for automation.
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
- All requests' reqBody and respBody are modeled using modified JSONSchema2PoPo2 (see my NyaMisty/JSONSchema2PoPo2), you can regenerate the binding by cd into `reqs/schemas` and execute `python3 -m schema_defs`
|
||||
- See more information on how to generate schema in reqs/schemas directory
|
||||
|
||||
## Credit
|
||||
|
||||
- Thanks @majd's ipatool, which is written in swift
|
||||
567
src_mac/ipatool-py/main.py
Executable file
567
src_mac/ipatool-py/main.py
Executable file
@@ -0,0 +1,567 @@
|
||||
#!/usr/bin/python3
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3 import Retry
|
||||
|
||||
from reqs.itunes import *
|
||||
from reqs.store import *
|
||||
import reprlib
|
||||
|
||||
reprlib.aRepr.maxstring = 200
|
||||
|
||||
import argparse
|
||||
|
||||
import logging
|
||||
from rich.logging import RichHandler
|
||||
from rich.console import Console
|
||||
import rich
|
||||
|
||||
rich.get_console().file = sys.stderr
|
||||
if rich.get_console().width < 100:
|
||||
rich.get_console().width = 100
|
||||
|
||||
logging_handler = RichHandler(rich_tracebacks=True)
|
||||
logging.basicConfig(
|
||||
level="INFO",
|
||||
format="%(message)s",
|
||||
datefmt="[%X]",
|
||||
handlers=[logging_handler]
|
||||
)
|
||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
retryLogger = logging.getLogger('urllib3.util.retry')
|
||||
retryLogger.setLevel(logging.DEBUG)
|
||||
retryLogger.handlers = [logging_handler]
|
||||
retryLogger.propagate = False
|
||||
|
||||
logger = logging.getLogger('main')
|
||||
|
||||
import requests
|
||||
|
||||
def get_zipinfo_datetime(timestamp=None):
|
||||
# Some applications need reproducible .whl files, but they can't do this without forcing
|
||||
# the timestamp of the individual ZipInfo objects. See issue #143.
|
||||
timestamp = int(timestamp or time.time())
|
||||
return time.gmtime(timestamp)[0:6]
|
||||
|
||||
def downloadFile(url, outfile, retries=10):
|
||||
for retry in range(retries):
|
||||
try:
|
||||
_downloadFile(url, outfile)
|
||||
break
|
||||
except Exception as e:
|
||||
logger.info("Error during downloading %s (retry %d/%d), error %s", url, retry, retries, e)
|
||||
os.remove(outfile)
|
||||
logger.info("Download success in retry %d", retry)
|
||||
|
||||
download_sess = requests.Session()
|
||||
download_sess.headers = {"User-Agent": CONFIGURATOR_UA}
|
||||
DOWNLOAD_READ_TIMEOUT = 25.0
|
||||
def _downloadFile(url, outfile):
|
||||
with download_sess.get(url, stream=True, timeout=DOWNLOAD_READ_TIMEOUT) as r:
|
||||
if not r.headers.get('Content-Length'):
|
||||
raise Exception("server is not returning Content-Length!")
|
||||
totalLen = int(r.headers.get('Content-Length', '0'))
|
||||
downLen = 0
|
||||
r.raise_for_status()
|
||||
try:
|
||||
with open(outfile, 'wb') as f:
|
||||
lastLen = 0
|
||||
for chunk in r.iter_content(chunk_size=1 * 1024 * 1024):
|
||||
# If you have chunk encoded response uncomment if
|
||||
# and set chunk_size parameter to None.
|
||||
# if chunk:
|
||||
f.write(chunk)
|
||||
downLen += len(chunk)
|
||||
if totalLen and downLen - lastLen > totalLen * 0.05:
|
||||
logger.info("Download progress: %3.2f%% (%5.1fM /%5.1fM)" % (
|
||||
downLen / totalLen * 100, downLen / 1024 / 1024, totalLen / 1024 / 1024))
|
||||
lastLen = downLen
|
||||
finally:
|
||||
if downLen != totalLen: # ensure no partial downloaded files exists
|
||||
os.unlink(outfile)
|
||||
if downLen != totalLen:
|
||||
raise Exception("failed to completely download the IPA file")
|
||||
|
||||
return outfile
|
||||
|
||||
class IPATool(object):
|
||||
def __init__(self):
|
||||
self.sess = requests.Session()
|
||||
|
||||
retry_strategy = Retry(
|
||||
connect=4,
|
||||
read=4,
|
||||
# total=8,
|
||||
status=20,
|
||||
allowed_methods=None,
|
||||
status_forcelist=[429, 502, 503],
|
||||
backoff_factor=1.0,
|
||||
respect_retry_after_header=False,
|
||||
)
|
||||
self.sess.mount("https://", HTTPAdapter(max_retries=retry_strategy))
|
||||
self.sess.mount("http://", HTTPAdapter(max_retries=retry_strategy))
|
||||
IPATOOL_PROXY = os.getenv("IPATOOL_PROXY")
|
||||
if IPATOOL_PROXY is not None:
|
||||
self.sess.proxies.update(
|
||||
{'http': IPATOOL_PROXY, 'https': IPATOOL_PROXY})
|
||||
# self.sess.headers = {}
|
||||
self.sess.headers = {"Connection": "close"}
|
||||
self.sess.keep_alive = False
|
||||
|
||||
self.appId = None
|
||||
# self.appInfo = None
|
||||
self.appVerId = None
|
||||
self.appVerIds = None
|
||||
|
||||
self.jsonOut = None
|
||||
|
||||
def tool_main(self):
|
||||
commparser = argparse.ArgumentParser(description='IPATool-Python Commands.', add_help=False)
|
||||
subp = commparser.add_subparsers(dest='command', required=True)
|
||||
lookup_p = subp.add_parser('lookup')
|
||||
id_group = lookup_p.add_mutually_exclusive_group(required=True)
|
||||
id_group.add_argument('--bundle-id', '-b', dest='bundle_id')
|
||||
id_group.add_argument('--appId', '-i', dest='appId')
|
||||
lookup_p.add_argument('--country', '-c', dest='country', required=True)
|
||||
lookup_p.add_argument('--get-verid', dest='get_verid', action='store_true')
|
||||
lookup_p.set_defaults(func=self.handleLookup)
|
||||
|
||||
def add_auth_options(p):
|
||||
auth_p = p.add_argument_group('Auth Options', 'Must specify either Apple ID & Password, or iTunes Server URL')
|
||||
appleid = auth_p.add_argument('--appleid', '-e')
|
||||
passwd = auth_p.add_argument('--password', '-p')
|
||||
sessdir = auth_p.add_argument('--session-dir', dest='session_dir', default=None)
|
||||
|
||||
itunessrv = auth_p.add_argument('--itunes-server', '-s', dest='itunes_server')
|
||||
|
||||
## Multiple hack here just to achieve (appleid & password) || itunes_server
|
||||
# p._optionals.conflict_handler = 'ignore'
|
||||
# p._optionals._handle_conflict_ignore = lambda *args: None
|
||||
auth_p = p.add_mutually_exclusive_group(required=True)
|
||||
auth_p._group_actions.append(appleid)
|
||||
auth_p._group_actions.append(itunessrv)
|
||||
# auth_p._group_actions.append(sessdir)
|
||||
|
||||
auth_p = p.add_mutually_exclusive_group(required=True)
|
||||
auth_p._group_actions.append(passwd)
|
||||
auth_p._group_actions.append(itunessrv)
|
||||
|
||||
purchase_p = subp.add_parser('purchase')
|
||||
add_auth_options(purchase_p)
|
||||
purchase_p.add_argument('--appId', '-i', dest='appId')
|
||||
purchase_p.set_defaults(func=self.handlePurchase)
|
||||
|
||||
down_p = subp.add_parser('download')
|
||||
add_auth_options(down_p)
|
||||
down_p.add_argument('--appId', '-i', dest='appId')
|
||||
down_p.add_argument('--appVerId', dest='appVerId')
|
||||
down_p.add_argument('--purchase', action='store_true')
|
||||
down_p.add_argument('--downloadAllVersion', action='store_true')
|
||||
down_p.add_argument('--output-dir', '-o', dest='output_dir', default='.')
|
||||
down_p.set_defaults(func=self.handleDownload)
|
||||
|
||||
his_p = subp.add_parser('historyver')
|
||||
his_p.add_argument('--appId', '-i', dest='appId')
|
||||
his_p.add_argument('--purchase', action='store_true')
|
||||
his_p.add_argument('--output-dir', '-o', dest='output_dir', default='.')
|
||||
add_auth_options(his_p)
|
||||
his_p.set_defaults(func=self.handleHistoryVersion)
|
||||
|
||||
parser = argparse.ArgumentParser(description='IPATool-Python.', parents=[commparser])
|
||||
parser.add_argument('--log-level', '-l', dest='log_level', default='info',
|
||||
help='output level (default: info)')
|
||||
parser.add_argument('--json', dest='out_json', action='store_true',
|
||||
help='output json in stdout (log will always be put into stderr)')
|
||||
|
||||
# parse global flags & first comm's arguments
|
||||
args, rest = parser.parse_known_args()
|
||||
logging.getLogger().setLevel(args.log_level.upper())
|
||||
outJson = args.out_json
|
||||
|
||||
while True:
|
||||
args.func(args)
|
||||
if not rest:
|
||||
break
|
||||
args, rest = commparser.parse_known_args(rest)
|
||||
|
||||
if outJson and self.jsonOut:
|
||||
print(json.dumps(self.jsonOut, ensure_ascii=False))
|
||||
|
||||
def _outputJson(self, obj):
|
||||
self.jsonOut = obj
|
||||
|
||||
def handleLookup(self, args):
|
||||
if args.bundle_id:
|
||||
s = 'BundleID %s' % args.bundle_id
|
||||
else:
|
||||
s = 'AppID %s' % args.appId
|
||||
logger.info('Looking up app in country %s with BundleID %s' % (args.country, s))
|
||||
iTunes = iTunesClient(self.sess)
|
||||
appInfos = iTunes.lookup(bundleId=args.bundle_id, appId=args.appId, country=args.country)
|
||||
if appInfos.resultCount != 1:
|
||||
logger.fatal("Failed to find app in country %s with %s" % (args.country, s))
|
||||
return
|
||||
appInfo = appInfos.results[0]
|
||||
logger.info("Found app:\n\tName: %s\n\tVersion: %s\n\tbundleId: %s\n\tappId: %s" % (appInfo.trackName, appInfo.version, appInfo.bundleId, appInfo.trackId))
|
||||
self.appId = appInfo.trackId
|
||||
# self.appInfo = appInfo
|
||||
|
||||
ret = {
|
||||
"name": appInfo.trackName,
|
||||
"version": appInfo.version,
|
||||
"appId": appInfo.trackId,
|
||||
"bundleId": appInfo.bundleId,
|
||||
}
|
||||
|
||||
if args.get_verid:
|
||||
logger.info("Retrieving verId using iTunes webpage...")
|
||||
verId = iTunes.getAppVerId(self.appId, args.country)
|
||||
logger.info("Got current verId using iTunes webpage: %s" % verId)
|
||||
ret["appVerId"] = verId
|
||||
|
||||
self._outputJson(ret)
|
||||
|
||||
storeClientCache = {}
|
||||
def _get_StoreClient(self, args):
|
||||
cachekey = args.itunes_server or args.appleid
|
||||
store, lastseen = self.storeClientCache.get(cachekey, (None, None,))
|
||||
if store:
|
||||
if time.time() - lastseen < 30.0:
|
||||
return store
|
||||
del self.storeClientCache[cachekey]
|
||||
|
||||
newSess = pickle.loads(pickle.dumps(self.sess))
|
||||
Store = StoreClient(newSess)
|
||||
|
||||
if args.itunes_server:
|
||||
logger.info("Using iTunes interface %s to download app!" % args.itunes_server)
|
||||
servUrl = args.itunes_server
|
||||
def handle_iTunes_provider(url):
|
||||
startTime = time.time()
|
||||
r = requests.get(servUrl, params={
|
||||
'url': url
|
||||
})
|
||||
logger.debug("got itunes header in %.2f seconds", time.time() - startTime)
|
||||
|
||||
ret = r.json()
|
||||
kbsync = bytes.fromhex(ret.pop('kbsync'))
|
||||
guid = ret.pop('guid')
|
||||
retHdrs = ret.pop('headers')
|
||||
handled = {
|
||||
'headers': retHdrs,
|
||||
'kbsync': kbsync,
|
||||
'guid': guid,
|
||||
}
|
||||
if 'sbsync' in ret:
|
||||
handled['sbsync'] = bytes.fromhex(ret.pop('sbsync'))
|
||||
if 'afds' in ret:
|
||||
handled['afds'] = ret.pop('afds')
|
||||
return handled
|
||||
Store.iTunes_provider = handle_iTunes_provider
|
||||
else:
|
||||
appleid = args.appleid
|
||||
applepass = args.password
|
||||
|
||||
needLogin = True
|
||||
session_cache = os.path.join(args.session_dir, args.appleid) if args.session_dir else None
|
||||
if session_cache and os.path.exists(session_cache):
|
||||
needLogin = False
|
||||
try:
|
||||
# inside try in case the file format changed
|
||||
with open(session_cache, "r") as f:
|
||||
content = f.read()
|
||||
Store.authenticate_load_session(content)
|
||||
except Exception as e:
|
||||
logger.warning(f"Error loading session {session_cache}")
|
||||
os.unlink(session_cache)
|
||||
needLogin = True
|
||||
else:
|
||||
logger.info('Loaded session for %s' % (str(Store.authInfo)))
|
||||
if needLogin:
|
||||
logger.info("Logging into iTunes as %s ..." % appleid)
|
||||
|
||||
Store.authenticate(appleid, applepass)
|
||||
logger.info('Logged in as %s' % (str(Store.authInfo)))
|
||||
|
||||
if session_cache:
|
||||
with open(session_cache, "w") as f:
|
||||
f.write(Store.authenticate_save_session())
|
||||
|
||||
def authedPost(*args, **kwargs):
|
||||
if 'MZFinance.woa/wa/authenticate' in args[0]:
|
||||
return Store.sess.original_post(*args, **kwargs)
|
||||
for i in range(3):
|
||||
r = Store.sess.original_post(*args, **kwargs)
|
||||
isAuthFail = False
|
||||
try:
|
||||
d = plistlib.loads(r.content)
|
||||
if str(d['failureType']) in ("2034", "1008"):
|
||||
isAuthFail = True
|
||||
except:
|
||||
return r
|
||||
if not isAuthFail:
|
||||
return r
|
||||
Store.authenticate(appleid, applepass)
|
||||
if session_cache:
|
||||
with open(session_cache, "w") as f:
|
||||
f.write(Store.authenticate_save_session())
|
||||
continue
|
||||
Store.sess.original_post = Store.sess.post
|
||||
Store.sess.post = authedPost
|
||||
|
||||
self.storeClientCache[cachekey] = (Store, time.time(),)
|
||||
return Store
|
||||
|
||||
def _handleStoreException(self, _e):
|
||||
e = _e # type: StoreException
|
||||
logger.fatal("Store %s failed! Message: %s%s" % (e.req, e.errMsg, " (errorType %s)" % e.errType if e.errType else ''))
|
||||
logger.fatal(" Raw Response: %s" % (e.resp))
|
||||
|
||||
def handlePurchase(self, args):
|
||||
Store = self._get_StoreClient(args)
|
||||
logger.info('Try to purchase appId %s' % (self.appId))
|
||||
try:
|
||||
Store.purchase(self.appId)
|
||||
except StoreException as e:
|
||||
if e.errMsg == 'purchased_before':
|
||||
logger.warning('You have already purchased appId %s before' % (self.appId))
|
||||
else:
|
||||
raise
|
||||
|
||||
def handleHistoryVersion(self, args, caches=True):
|
||||
if args.appId:
|
||||
self.appId = args.appId
|
||||
|
||||
if not self.appId:
|
||||
logger.fatal("appId not supplied!")
|
||||
return
|
||||
|
||||
versionsJsonPath = args.output_dir + f"/historyver_{self.appId}.json"
|
||||
if caches:
|
||||
if os.path.exists(versionsJsonPath):
|
||||
cacheContent = None
|
||||
try:
|
||||
with open(versionsJsonPath) as f:
|
||||
cacheContent = json.load(f)
|
||||
except:
|
||||
pass
|
||||
if cacheContent is not None:
|
||||
logger.info('Loaded history version cache for appId %s' % self.appId)
|
||||
self.appVerIds = cacheContent['appVerIds']
|
||||
return
|
||||
|
||||
logger.info('Retrieving history version for appId %s' % self.appId)
|
||||
|
||||
try:
|
||||
Store = self._get_StoreClient(args)
|
||||
|
||||
logger.info('Retrieving download info for appId %s' % (self.appId))
|
||||
if args.purchase:
|
||||
logger.info('Purchasing appId %s' % (self.appId))
|
||||
# We have already successfully purchased, so don't purchase again :)
|
||||
self.handlePurchase(args)
|
||||
args.purchase = False
|
||||
|
||||
downResp = Store.download(self.appId, '', isRedownload=not args.purchase)
|
||||
logger.debug('Got download info: %s', downResp)
|
||||
if args.purchase:
|
||||
# We have already successfully purchased, so don't purchase again :)
|
||||
args.purchase = False
|
||||
|
||||
if not downResp.songList:
|
||||
logger.fatal("failed to get app download info!")
|
||||
raise StoreException('download', downResp, 'no songList')
|
||||
downInfo = downResp.songList[0]
|
||||
logger.info('Got available version ids %s', downInfo.metadata.softwareVersionExternalIdentifiers)
|
||||
self._outputJson({
|
||||
"appVerIds": downInfo.metadata.softwareVersionExternalIdentifiers
|
||||
})
|
||||
self.appVerIds = downInfo.metadata.softwareVersionExternalIdentifiers
|
||||
if caches:
|
||||
with open(versionsJsonPath, 'w') as f:
|
||||
json.dump({
|
||||
'appVerIds': self.appVerIds,
|
||||
}, f)
|
||||
except StoreException as e:
|
||||
self._handleStoreException(e)
|
||||
if not e.errMsg.startswith('http error status') and not e.errMsg.startswith(
|
||||
'We are temporarily unable to process your request') and not e.errMsg.startswith(
|
||||
"License not found"):
|
||||
# this error is persistent (e.g. app deleted)
|
||||
self.appVerIds = []
|
||||
if caches:
|
||||
with open(versionsJsonPath, 'w') as f:
|
||||
json.dump({
|
||||
'appVerIds': self.appVerIds,
|
||||
'error': str(e),
|
||||
'errorResp': str(e.resp),
|
||||
}, f)
|
||||
|
||||
def handleDownload(self, args):
|
||||
os.makedirs(args.output_dir, exist_ok=True)
|
||||
if args.downloadAllVersion:
|
||||
if os.path.exists(args.output_dir + "/all_done"):
|
||||
logger.info('Already fully finished, skipping!')
|
||||
return
|
||||
self.handleHistoryVersion(args, caches=True)
|
||||
if not self.appVerIds:
|
||||
logger.fatal('failed to retrive history versions for appId %s', args.appId)
|
||||
return
|
||||
everything_succ = True
|
||||
for appVerId in self.appVerIds:
|
||||
self.jsonOut = None
|
||||
stateFile = args.output_dir + '/' + str(appVerId) + '.finish'
|
||||
if os.path.exists(stateFile):
|
||||
logger.info('Skipping already downloaded')
|
||||
continue
|
||||
try:
|
||||
self.appVerId = appVerId
|
||||
self.downloadOne(args)
|
||||
if args.out_json and self.jsonOut:
|
||||
print(json.dumps(self.jsonOut, ensure_ascii=False))
|
||||
if self.jsonOut is not None: # successfully finished
|
||||
with open(stateFile, 'w') as f:
|
||||
f.write('1')
|
||||
except Exception as e:
|
||||
logger.fatal("error during downloading appVerId %s", appVerId, exc_info=1)
|
||||
everything_succ = False
|
||||
finally:
|
||||
self.jsonOut = None
|
||||
if everything_succ:
|
||||
with open(args.output_dir + "/all_done", 'w') as f:
|
||||
f.write("1")
|
||||
else:
|
||||
self.downloadOne(args)
|
||||
|
||||
def downloadOne(self, args):
|
||||
if args.appId:
|
||||
self.appId = args.appId
|
||||
if args.appVerId:
|
||||
self.appVerId = args.appVerId
|
||||
|
||||
if not self.appId:
|
||||
logger.fatal("appId not supplied!")
|
||||
return
|
||||
|
||||
logger.info("Downloading appId %s appVerId %s", self.appId, self.appVerId)
|
||||
try:
|
||||
appleid = args.appleid
|
||||
Store = self._get_StoreClient(args)
|
||||
|
||||
if args.purchase:
|
||||
self.handlePurchase(args)
|
||||
|
||||
logger.info('Retrieving download info for appId %s%s' % (self.appId, " with versionId %s" % self.appVerId if self.appVerId else ""))
|
||||
|
||||
downResp = Store.download(self.appId, self.appVerId, isRedownload=not args.purchase)
|
||||
logger.debug('Got download info: %s', downResp.as_dict())
|
||||
|
||||
if not downResp.songList:
|
||||
logger.fatal("failed to get app download info!")
|
||||
raise StoreException('download', downResp, 'no songList')
|
||||
downInfo = downResp.songList[0]
|
||||
|
||||
appName = downInfo.metadata.bundleDisplayName
|
||||
appId = downInfo.songId
|
||||
appBundleId = downInfo.metadata.softwareVersionBundleId
|
||||
appVerId = downInfo.metadata.softwareVersionExternalIdentifier
|
||||
# when downloading history versions, bundleShortVersionString will always give a wrong version number (the newest one)
|
||||
# should use bundleVersion in these cases
|
||||
appVer = downInfo.metadata.bundleShortVersionString if not self.appVerId else downInfo.metadata.bundleVersion
|
||||
|
||||
logger.info(f'Downloading app {appName} ({appBundleId}) with appId {appId} (version {appVer}, versionId {appVerId})')
|
||||
|
||||
# if self.appInfo:
|
||||
filename = '%s-%s-%s-%s.ipa' % (appBundleId,
|
||||
appVer,
|
||||
appId,
|
||||
appVerId)
|
||||
# else:
|
||||
# filename = '%s-%s.ipa' % (self.appId, appVerId)
|
||||
|
||||
filepath = os.path.join(args.output_dir, filename)
|
||||
logger.info("Downloading ipa to %s" % filepath)
|
||||
downloadFile(downInfo.URL, filepath)
|
||||
metadata = downInfo.metadata.as_dict()
|
||||
if appleid:
|
||||
metadata["apple-id"] = appleid
|
||||
metadata["userName"] = appleid
|
||||
logger.info("Writing out iTunesMetadata.plist...")
|
||||
if zipfile.is_zipfile(filepath):
|
||||
with zipfile.ZipFile(filepath, 'a') as ipaFile:
|
||||
logger.debug("Writing iTunesMetadata.plist")
|
||||
ipaFile.writestr(zipfile.ZipInfo("iTunesMetadata.plist", get_zipinfo_datetime()), plistlib.dumps(metadata))
|
||||
logger.debug("Writing IPAToolInfo.plist")
|
||||
ipaFile.writestr(zipfile.ZipInfo("IPAToolInfo.plist", get_zipinfo_datetime()), plistlib.dumps(downResp.as_dict()))
|
||||
|
||||
def findAppContentPath(c):
|
||||
if not c.startswith('Payload/'):
|
||||
return False
|
||||
pathparts = c.strip('/').split('/')
|
||||
if len(pathparts) != 2:
|
||||
return False
|
||||
if not pathparts[1].endswith(".app"):
|
||||
return False
|
||||
return True
|
||||
appContentDirChoices = [c for c in ipaFile.namelist() if findAppContentPath(c)]
|
||||
if len(appContentDirChoices) != 1:
|
||||
raise Exception("failed to find appContentDir, choices %s", appContentDirChoices)
|
||||
appContentDir = appContentDirChoices[0].rstrip('/')
|
||||
|
||||
processedSinf = False
|
||||
if (appContentDir + '/SC_Info/Manifest.plist') in ipaFile.namelist():
|
||||
#Try to get the Manifest.plist file, since it doesn't always exist.
|
||||
scManifestData = ipaFile.read(appContentDir + '/SC_Info/Manifest.plist')
|
||||
logger.debug("Got SC_Info/Manifest.plist: %s", scManifestData)
|
||||
scManifest = plistlib.loads(scManifestData)
|
||||
sinfs = {c.id: c.sinf for c in downInfo.sinfs}
|
||||
if 'SinfPaths' in scManifest:
|
||||
for i, sinfPath in enumerate(scManifest['SinfPaths']):
|
||||
logger.debug("Writing sinf to %s", sinfPath)
|
||||
ipaFile.writestr(appContentDir + '/' + sinfPath, sinfs[i])
|
||||
processedSinf = True
|
||||
if not processedSinf:
|
||||
logger.info('Manifest.plist does not exist! Assuming it is an old app without one...')
|
||||
infoListData = ipaFile.read(appContentDir + '/Info.plist') #Is this not loaded anywhere yet?
|
||||
infoList = plistlib.loads(infoListData)
|
||||
sinfPath = appContentDir + '/SC_Info/'+infoList['CFBundleExecutable']+".sinf"
|
||||
logger.debug("Writing sinf to %s", sinfPath)
|
||||
#Assuming there is only one .sinf file, hence the 0
|
||||
ipaFile.writestr(sinfPath, downInfo.sinfs[0].sinf)
|
||||
processedSinf = True
|
||||
|
||||
logger.info("Downloaded ipa to %s" % filename)
|
||||
else:
|
||||
plist = filepath[:-4]+".info.plist"
|
||||
with open(plist, "wb") as f:
|
||||
f.write(plistlib.dumps(downResp.as_dict()))
|
||||
plist = filepath[:-4]+".plist"
|
||||
with open(plist, "wb") as f:
|
||||
f.write(plistlib.dumps(metadata))
|
||||
logger.info("Downloaded ipa to %s and plist to %s" % (filename, plist))
|
||||
|
||||
self._outputJson({
|
||||
"appName": appName,
|
||||
"appBundleId": appBundleId,
|
||||
"appVer": appVer,
|
||||
"appId": appId,
|
||||
"appVerId": appVerId,
|
||||
|
||||
"downloadedIPA": filepath,
|
||||
"downloadedVerId": appVerId,
|
||||
"downloadURL": downInfo.URL,
|
||||
})
|
||||
except StoreException as e:
|
||||
self._handleStoreException(e)
|
||||
|
||||
def main():
|
||||
tool = IPATool()
|
||||
tool.tool_main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
0
src_mac/ipatool-py/reqs/__init__.py
Executable file
0
src_mac/ipatool-py/reqs/__init__.py
Executable file
47
src_mac/ipatool-py/reqs/itunes.py
Executable file
47
src_mac/ipatool-py/reqs/itunes.py
Executable file
@@ -0,0 +1,47 @@
|
||||
__all__ = ['iTunesClient']
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
from reqs.schemas.itunes_lookup_resp import ItunesLookupResp
|
||||
import requests
|
||||
|
||||
STORE_TABLE = {"AE":"143481-2,32","AG":"143540-2,32","AI":"143538-2,32","AL":"143575-2,32","AM":"143524-2,32","AO":"143564-2,32","AR":"143505-28,32","AT":"143445-4,32","AU":"143460-27,32","AZ":"143568-2,32","BB":"143541-2,32","BE":"143446-2,32","BF":"143578-2,32","BG":"143526-2,32","BH":"143559-2,32","BJ":"143576-2,32","BM":"143542-2,32","BN":"143560-2,32","BO":"143556-28,32","BR":"143503-15,32","BS":"143539-2,32","BT":"143577-2,32","BW":"143525-2,32","BY":"143565-2,32","BZ":"143555-2,32","CA":"143455-6,32","CG":"143582-2,32","CH":"143459-57,32","CL":"143483-28,32","CN":"143465-19,32","CO":"143501-28,32","CR":"143495-28,32","CV":"143580-2,32","CY":"143557-2,32","CZ":"143489-2,32","DE":"143443-4,32","DK":"143458-2,32","DM":"143545-2,32","DO":"143508-28,32","DZ":"143563-2,32","EC":"143509-28,32","EE":"143518-2,32","EG":"143516-2,32","ES":"143454-8,32","FI":"143447-2,32","FJ":"143583-2,32","FM":"143591-2,32","FR":"143442-3,32","GB":"143444-2,32","GD":"143546-2,32","GH":"143573-2,32","GM":"143584-2,32","GR":"143448-2,32","GT":"143504-28,32","GW":"143585-2,32","GY":"143553-2,32","HK":"143463-45,32","HN":"143510-28,32","HR":"143494-2,32","HU":"143482-2,32","ID":"143476-2,32","IE":"143449-2,32","IL":"143491-2,32","IN":"143467-2,32","IS":"143558-2,32","IT":"143450-7,32","JM":"143511-2,32","JO":"143528-2,32","JP":"143462-9,32","KE":"143529-2,32","KG":"143586-2,32","KH":"143579-2,32","KN":"143548-2,32","KR":"143466-13,32","KW":"143493-2,32","KY":"143544-2,32","KZ":"143517-2,32","LA":"143587-2,32","LB":"143497-2,32","LC":"143549-2,32","LK":"143486-2,32","LR":"143588-2,32","LT":"143520-2,32","LU":"143451-2,32","LV":"143519-2,32","MD":"143523-2,32","MG":"143531-2,32","MK":"143530-2,32","ML":"143532-2,32","MN":"143592-2,32","MO":"143515-45,32","MR":"143590-2,32","MS":"143547-2,32","MT":"143521-2,32","MU":"143533-2,32","MW":"143589-2,32","MX":"143468-28,32","MY":"143473-2,32","MZ":"143593-2,32","NA":"143594-2,32","NE":"143534-2,32","NG":"143561-2,32","NI":"143512-28,32","NL":"143452-10,32","NO":"143457-2,32","NP":"143484-2,32","NZ":"143461-27,32","OM":"143562-2,32","PA":"143485-28,32","PE":"143507-28,32","PG":"143597-2,32","PH":"143474-2,32","PK":"143477-2,32","PL":"143478-2,32","PT":"143453-24,32","PW":"143595-2,32","PY":"143513-28,32","QA":"143498-2,32","RO":"143487-2,32","RU":"143469-16,32","SA":"143479-2,32","SB":"143601-2,32","SC":"143599-2,32","SE":"143456-17,32","SG":"143464-19,32","SI":"143499-2,32","SK":"143496-2,32","SL":"143600-2,32","SN":"143535-2,32","SR":"143554-2,32","ST":"143598-2,32","SV":"143506-28,32","SZ":"143602-2,32","TC":"143552-2,32","TD":"143581-2,32","TH":"143475-2,32","TJ":"143603-2,32","TM":"143604-2,32","TN":"143536-2,32","TR":"143480-2,32","TT":"143551-2,32","TW":"143470-18,32","TZ":"143572-2,32","UA":"143492-2,32","UG":"143537-2,32","US":"143441-1,32","UY":"143514-2,32","UZ":"143566-2,32","VC":"143550-2,32","VE":"143502-28,32","VG":"143543-2,32","VN":"143471-2,32","YE":"143571-2,32","ZA":"143472-2,32","ZW":"143605-2,32"}
|
||||
|
||||
|
||||
class iTunesClient(object):
|
||||
def __init__(self, sess: requests.Session):
|
||||
self.sess = sess
|
||||
|
||||
# curl -k -X GET \
|
||||
# -H "Content-Type: application/x-www-form-urlencoded" \
|
||||
# https://itunes.apple.com/lookup?bundleId=com.touchingapp.potatsolite&limit=1&media=software
|
||||
def lookup(self, bundleId=None, appId=None, term=None, country="US", limit=1, media="software") -> ItunesLookupResp:
|
||||
r = self.sess.get("https://itunes.apple.com/lookup?",
|
||||
params={
|
||||
"bundleId": bundleId,
|
||||
"id": appId,
|
||||
"term": term,
|
||||
"country": country,
|
||||
"limit": limit,
|
||||
"media": media,
|
||||
},
|
||||
headers={
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
})
|
||||
return ItunesLookupResp.from_dict(r.json())
|
||||
|
||||
def getAppVerId(self, appId, country):
|
||||
if not ',' in country:
|
||||
storeFront = STORE_TABLE[country.upper()]
|
||||
else:
|
||||
storeFront = country
|
||||
appInfo = requests.get("https://apps.apple.com/app/id%s" % appId, headers={"X-Apple-Store-Front": storeFront}).text
|
||||
try:
|
||||
appParam = re.findall(r'"buyParams":"(.*?)"', appInfo)[0]
|
||||
except:
|
||||
appParam = re.findall(r'buy-params="(.*?)"', appInfo)[0]
|
||||
appParam = appParam.replace('&', '&')
|
||||
appParamDict = dict((c.split('=') for c in json.loads('"%s"' % appParam).split('&')))
|
||||
appVer = appParamDict['appExtVrsId']
|
||||
return appVer
|
||||
18
src_mac/ipatool-py/reqs/schemas/README.md
Executable file
18
src_mac/ipatool-py/reqs/schemas/README.md
Executable file
@@ -0,0 +1,18 @@
|
||||
# Schema Definitions for ipatool-py
|
||||
|
||||
In this directory, JSON Schema Definition files are stored in `schema_defs`, with corresponding plist examples in `schema_examples`
|
||||
|
||||
## How to generate schema from plist
|
||||
|
||||
1. Convert plist to corresponding JSON, using either `plistlib` or online tools
|
||||
2. Use https://www.liquid-technologies.com/online-json-to-schema-converter to convert JSON to schema
|
||||
- You have to switch the `Array Rules` option to `List Validation`
|
||||
3. Merge different request / response body's schema manually
|
||||
- Usually the "required" field can help you merging the json schema
|
||||
|
||||
## Regenerate schema bindings
|
||||
|
||||
Run this in current folder:
|
||||
```
|
||||
python3 -m schema_defs
|
||||
```
|
||||
0
src_mac/ipatool-py/reqs/schemas/__init__.py
Executable file
0
src_mac/ipatool-py/reqs/schemas/__init__.py
Executable file
1593
src_mac/ipatool-py/reqs/schemas/itunes_lookup_resp.py
Executable file
1593
src_mac/ipatool-py/reqs/schemas/itunes_lookup_resp.py
Executable file
File diff suppressed because it is too large
Load Diff
0
src_mac/ipatool-py/reqs/schemas/schema_defs/__init__.py
Executable file
0
src_mac/ipatool-py/reqs/schemas/schema_defs/__init__.py
Executable file
12
src_mac/ipatool-py/reqs/schemas/schema_defs/__main__.py
Executable file
12
src_mac/ipatool-py/reqs/schemas/schema_defs/__main__.py
Executable file
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
import os.path as path
|
||||
import subprocess
|
||||
|
||||
curpath = path.dirname(__file__)
|
||||
outpath = path.dirname(curpath)
|
||||
for p in os.listdir(curpath):
|
||||
if not p.endswith('.json'):
|
||||
continue
|
||||
#subprocess.call(["jsonschema2popo2", "--translate-properties", "--use-types", "-o", path.join(outpath, p.split('.')[0] + '.py'), path.join(curpath, p)])
|
||||
print("Converting %s" % p)
|
||||
subprocess.call(["jsonschema2popo2", "--use-types", "-o", path.join(outpath, p.split('.')[0] + '.py'), path.join(curpath, p)])
|
||||
222
src_mac/ipatool-py/reqs/schemas/schema_defs/itunes_lookup_resp.json
Executable file
222
src_mac/ipatool-py/reqs/schemas/schema_defs/itunes_lookup_resp.json
Executable file
@@ -0,0 +1,222 @@
|
||||
{
|
||||
"title": "iTunes Lookup Resp",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resultCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"results": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appletvScreenshotUrls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["number","string","boolean","object","array", "null"]
|
||||
}
|
||||
},
|
||||
"screenshotUrls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ipadScreenshotUrls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"artworkUrl60": {
|
||||
"type": "string"
|
||||
},
|
||||
"artworkUrl512": {
|
||||
"type": "string"
|
||||
},
|
||||
"artworkUrl100": {
|
||||
"type": "string"
|
||||
},
|
||||
"artistViewUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"supportedDevices": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"advisories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["number","string","boolean","object","array", "null"]
|
||||
}
|
||||
},
|
||||
"isGameCenterEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"features": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"minimumOsVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"trackCensoredName": {
|
||||
"type": "string"
|
||||
},
|
||||
"languageCodesISO2A": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"fileSizeBytes": {
|
||||
"type": "string"
|
||||
},
|
||||
"formattedPrice": {
|
||||
"type": "string"
|
||||
},
|
||||
"contentAdvisoryRating": {
|
||||
"type": "string"
|
||||
},
|
||||
"averageUserRatingForCurrentVersion": {
|
||||
"type": "number"
|
||||
},
|
||||
"userRatingCountForCurrentVersion": {
|
||||
"type": "integer"
|
||||
},
|
||||
"averageUserRating": {
|
||||
"type": "number"
|
||||
},
|
||||
"trackViewUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"trackContentRating": {
|
||||
"type": "string"
|
||||
},
|
||||
"releaseDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"genreIds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"primaryGenreName": {
|
||||
"type": "string"
|
||||
},
|
||||
"trackId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trackName": {
|
||||
"type": "string"
|
||||
},
|
||||
"sellerName": {
|
||||
"type": "string"
|
||||
},
|
||||
"isVppDeviceBasedLicensingEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"currentVersionReleaseDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"releaseNotes": {
|
||||
"type": "string"
|
||||
},
|
||||
"primaryGenreId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"currency": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"wrapperType": {
|
||||
"type": "string"
|
||||
},
|
||||
"artistId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"artistName": {
|
||||
"type": "string"
|
||||
},
|
||||
"genres": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"price": {
|
||||
"type": "number"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"bundleId": {
|
||||
"type": "string"
|
||||
},
|
||||
"userRatingCount": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"appletvScreenshotUrls",
|
||||
"screenshotUrls",
|
||||
"ipadScreenshotUrls",
|
||||
"artworkUrl60",
|
||||
"artworkUrl512",
|
||||
"artworkUrl100",
|
||||
"artistViewUrl",
|
||||
"supportedDevices",
|
||||
"advisories",
|
||||
"isGameCenterEnabled",
|
||||
"kind",
|
||||
"features",
|
||||
"minimumOsVersion",
|
||||
"trackCensoredName",
|
||||
"languageCodesISO2A",
|
||||
"fileSizeBytes",
|
||||
"formattedPrice",
|
||||
"contentAdvisoryRating",
|
||||
"averageUserRatingForCurrentVersion",
|
||||
"userRatingCountForCurrentVersion",
|
||||
"averageUserRating",
|
||||
"trackViewUrl",
|
||||
"trackContentRating",
|
||||
"releaseDate",
|
||||
"genreIds",
|
||||
"primaryGenreName",
|
||||
"trackId",
|
||||
"trackName",
|
||||
"sellerName",
|
||||
"isVppDeviceBasedLicensingEnabled",
|
||||
"currentVersionReleaseDate",
|
||||
"releaseNotes",
|
||||
"primaryGenreId",
|
||||
"currency",
|
||||
"version",
|
||||
"wrapperType",
|
||||
"artistId",
|
||||
"artistName",
|
||||
"genres",
|
||||
"price",
|
||||
"description",
|
||||
"bundleId",
|
||||
"userRatingCount"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"resultCount",
|
||||
"results"
|
||||
]
|
||||
}
|
||||
37
src_mac/ipatool-py/reqs/schemas/schema_defs/store_authenticate_req.json
Executable file
37
src_mac/ipatool-py/reqs/schemas/schema_defs/store_authenticate_req.json
Executable file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"title": "Store Authenticate Req",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appleId": {
|
||||
"type": "string"
|
||||
},
|
||||
"attempt": {
|
||||
"type": "string"
|
||||
},
|
||||
"createSession": {
|
||||
"type": "string"
|
||||
},
|
||||
"guid": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"rmp": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"appleId",
|
||||
"attempt",
|
||||
"createSession",
|
||||
"guid",
|
||||
"password",
|
||||
"rmp",
|
||||
"why"
|
||||
]
|
||||
}
|
||||
324
src_mac/ipatool-py/reqs/schemas/schema_defs/store_authenticate_resp.json
Executable file
324
src_mac/ipatool-py/reqs/schemas/schema_defs/store_authenticate_resp.json
Executable file
@@ -0,0 +1,324 @@
|
||||
{
|
||||
"title": "Store Authenticate Resp",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["number","string","boolean","object","array", "null"]
|
||||
}
|
||||
},
|
||||
"cancel-purchase-batch": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"customerMessage": {
|
||||
"type": "string"
|
||||
},
|
||||
"failureType": {
|
||||
"type": "string"
|
||||
},
|
||||
"accountInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appleId": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"firstName": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"firstName",
|
||||
"lastName"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"appleId",
|
||||
"address"
|
||||
]
|
||||
},
|
||||
"passwordToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"clearToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"m-allowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is-cloud-enabled": {
|
||||
"type": "string"
|
||||
},
|
||||
"dsPersonId": {
|
||||
"type": "string"
|
||||
},
|
||||
"creditDisplay": {
|
||||
"type": "string"
|
||||
},
|
||||
"creditBalance": {
|
||||
"type": "string"
|
||||
},
|
||||
"freeSongBalance": {
|
||||
"type": "string"
|
||||
},
|
||||
"isManagedStudent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"action": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind"
|
||||
]
|
||||
},
|
||||
"subscriptionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"terms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"latestTerms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"agreedToTerms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"latestTerms",
|
||||
"agreedToTerms",
|
||||
"source"
|
||||
]
|
||||
}
|
||||
},
|
||||
"account": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isMinor": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"suspectUnderage": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isMinor",
|
||||
"suspectUnderage"
|
||||
]
|
||||
},
|
||||
"family": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hasFamily": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"hasFamily"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"terms",
|
||||
"account",
|
||||
"family"
|
||||
]
|
||||
},
|
||||
"accountFlags": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"personalization": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"underThirteen": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"identityLastVerified": {
|
||||
"type": "integer"
|
||||
},
|
||||
"verifiedExpirationDate": {
|
||||
"type": "integer"
|
||||
},
|
||||
"retailDemo": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"autoPlay": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isDisabledAccount": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isRestrictedAccount": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isManagedAccount": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isInRestrictedRegion": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"accountFlagsVersion": {
|
||||
"type": "integer"
|
||||
},
|
||||
"hasAgreedToTerms": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hasAgreedToAppClipTerms": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hasWatchHardwareOffer": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isInFamily": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hasSubscriptionFamilySharingEnabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"personalization",
|
||||
"underThirteen",
|
||||
"identityLastVerified",
|
||||
"verifiedExpirationDate",
|
||||
"retailDemo",
|
||||
"autoPlay",
|
||||
"isDisabledAccount",
|
||||
"isRestrictedAccount",
|
||||
"isManagedAccount",
|
||||
"isInRestrictedRegion",
|
||||
"accountFlagsVersion",
|
||||
"hasAgreedToTerms",
|
||||
"hasAgreedToAppClipTerms",
|
||||
"hasWatchHardwareOffer",
|
||||
"isInFamily",
|
||||
"hasSubscriptionFamilySharingEnabled"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": "integer"
|
||||
},
|
||||
"download-queue-info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"download-queue-item-count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dsid": {
|
||||
"type": "integer"
|
||||
},
|
||||
"is-auto-download-machine": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"download-queue-item-count",
|
||||
"dsid",
|
||||
"is-auto-download-machine"
|
||||
]
|
||||
},
|
||||
"privacyAcknowledgement": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"com.apple.onboarding.appstore": {
|
||||
"type": "integer"
|
||||
},
|
||||
"com.apple.onboarding.applemusic": {
|
||||
"type": "integer"
|
||||
},
|
||||
"com.apple.onboarding.videos": {
|
||||
"type": "integer"
|
||||
},
|
||||
"com.apple.onboarding.itunesstore": {
|
||||
"type": "integer"
|
||||
},
|
||||
"com.apple.onboarding.itunesu": {
|
||||
"type": "integer"
|
||||
},
|
||||
"com.apple.onboarding.applearcade": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"com.apple.onboarding.appstore",
|
||||
"com.apple.onboarding.applemusic",
|
||||
"com.apple.onboarding.videos",
|
||||
"com.apple.onboarding.itunesstore",
|
||||
"com.apple.onboarding.itunesu",
|
||||
"com.apple.onboarding.applearcade"
|
||||
]
|
||||
},
|
||||
"dialog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"m-allowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
"defaultButton": {
|
||||
"type": "string"
|
||||
},
|
||||
"okButtonString": {
|
||||
"type": "string"
|
||||
},
|
||||
"initialCheckboxValue": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"m-allowed",
|
||||
"message",
|
||||
"explanation",
|
||||
"defaultButton",
|
||||
"okButtonString",
|
||||
"initialCheckboxValue"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pings",
|
||||
"accountInfo",
|
||||
"passwordToken",
|
||||
"clearToken",
|
||||
"m-allowed",
|
||||
"is-cloud-enabled",
|
||||
"dsPersonId",
|
||||
"creditDisplay",
|
||||
"creditBalance",
|
||||
"freeSongBalance",
|
||||
"isManagedStudent",
|
||||
"action",
|
||||
"subscriptionStatus",
|
||||
"accountFlags",
|
||||
"status",
|
||||
"download-queue-info",
|
||||
"privacyAcknowledgement",
|
||||
"dialog"
|
||||
]
|
||||
}
|
||||
120
src_mac/ipatool-py/reqs/schemas/schema_defs/store_buyproduct_req.json
Executable file
120
src_mac/ipatool-py/reqs/schemas/schema_defs/store_buyproduct_req.json
Executable file
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"title": "Store BuyProduct Req",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ageCheck": {
|
||||
"type": "string"
|
||||
},
|
||||
"appExtVrsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"guid": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasBeenAuthedForBuy": {
|
||||
"type": "string"
|
||||
},
|
||||
"isInApp": {
|
||||
"type": "string"
|
||||
},
|
||||
"kbsync": {
|
||||
"type": "string"
|
||||
},
|
||||
"sbsync": {
|
||||
"type": "string"
|
||||
},
|
||||
"afds": {
|
||||
"type": "string"
|
||||
},
|
||||
"machineName": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtApp": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtClientId": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtEventTime": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtPageId": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtPageType": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtPrevPage": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtRequestId": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtTopic": {
|
||||
"type": "string"
|
||||
},
|
||||
"needDiv": {
|
||||
"type": "string"
|
||||
},
|
||||
"pg": {
|
||||
"type": "string"
|
||||
},
|
||||
"price": {
|
||||
"type": "string"
|
||||
},
|
||||
"pricingParameters": {
|
||||
"type": "string"
|
||||
},
|
||||
"productType": {
|
||||
"type": "string"
|
||||
},
|
||||
"salableAdamId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
"hasAskedToFulfillPreorder": {
|
||||
"type": "string"
|
||||
},
|
||||
"buyWithoutAuthorization": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasDoneAgeCheck": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasConfirmedPaymentSheet": {
|
||||
"type": "string"
|
||||
},
|
||||
"asn": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"guid",
|
||||
"kbsync",
|
||||
"price",
|
||||
"pricingParameters",
|
||||
"productType",
|
||||
"salableAdamId",
|
||||
"appExtVrsId"
|
||||
],
|
||||
"optional": [
|
||||
"ageCheck",
|
||||
"hasBeenAuthedForBuy",
|
||||
"hasConfirmedPaymentSheet",
|
||||
"asn",
|
||||
"isInApp",
|
||||
|
||||
"machineName",
|
||||
"mtApp",
|
||||
"mtClientId",
|
||||
"mtEventTime",
|
||||
"mtPageId",
|
||||
"mtPageType",
|
||||
"mtPrevPage",
|
||||
"mtRequestId",
|
||||
"mtTopic",
|
||||
"needDiv",
|
||||
"pg"
|
||||
]
|
||||
}
|
||||
732
src_mac/ipatool-py/reqs/schemas/schema_defs/store_buyproduct_resp.json
Executable file
732
src_mac/ipatool-py/reqs/schemas/schema_defs/store_buyproduct_resp.json
Executable file
@@ -0,0 +1,732 @@
|
||||
{
|
||||
"title": "Store BuyProduct Resp",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["number","string","boolean","object","array", "null"]
|
||||
}
|
||||
},
|
||||
"jingleDocType": {
|
||||
"type": "string"
|
||||
},
|
||||
"jingleAction": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dsPersonId": {
|
||||
"type": "string"
|
||||
},
|
||||
"creditDisplay": {
|
||||
"type": "string"
|
||||
},
|
||||
"creditBalance": {
|
||||
"type": "string"
|
||||
},
|
||||
"freeSongBalance": {
|
||||
"type": "string"
|
||||
},
|
||||
"creditDisplayInternal": {
|
||||
"type": "string"
|
||||
},
|
||||
"authorized": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"download-queue-item-count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"songList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"songId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"URL": {
|
||||
"type": "string"
|
||||
},
|
||||
"downloadKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"artworkURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"artwork-urls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image-type": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"image-type",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
"md5": {
|
||||
"type": "string"
|
||||
},
|
||||
"chunks": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chunkSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"hashes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chunkSize",
|
||||
"hashes"
|
||||
]
|
||||
},
|
||||
"isStreamable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"uncompressedSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sinfs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sinf": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"sinf"
|
||||
]
|
||||
}
|
||||
},
|
||||
"purchaseDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"download-id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is-in-queue": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"asset-info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file-size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"flavor": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file-size",
|
||||
"flavor"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"MacUIRequiredDeviceCapabilities": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"arm64": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
"UIRequiredDeviceCapabilities": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"arm64": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
"WKRunsIndependentlyOfCompanionApp": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"WKWatchOnly": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"appleWatchEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"artistId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"artistName": {
|
||||
"type": "string"
|
||||
},
|
||||
"bundleDisplayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"bundleShortVersionString": {
|
||||
"type": "string"
|
||||
},
|
||||
"bundleVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"copyright": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileExtension": {
|
||||
"type": "string"
|
||||
},
|
||||
"gameCenterEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"gameCenterEverEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"genre": {
|
||||
"type": "string"
|
||||
},
|
||||
"genreId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"itemId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"itemName": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"nameTranscriptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"zh-Hans-CN": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"zh-Hans-CN"
|
||||
]
|
||||
},
|
||||
"playlistName": {
|
||||
"type": "string"
|
||||
},
|
||||
"product-type": {
|
||||
"type": "string"
|
||||
},
|
||||
"rating": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"rank": {
|
||||
"type": "integer"
|
||||
},
|
||||
"system": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"content",
|
||||
"label",
|
||||
"rank",
|
||||
"system"
|
||||
]
|
||||
},
|
||||
"releaseDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"requiresRosetta": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"runsOnAppleSilicon": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"runsOnIntel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"s": {
|
||||
"type": "integer"
|
||||
},
|
||||
"software-platform": {
|
||||
"type": "string"
|
||||
},
|
||||
"softwareIcon57x57URL": {
|
||||
"type": "string"
|
||||
},
|
||||
"softwareIconNeedsShine": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"softwareSupportedDeviceIds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"softwareVersionBundleId": {
|
||||
"type": "string"
|
||||
},
|
||||
"softwareVersionExternalIdentifier": {
|
||||
"type": "integer"
|
||||
},
|
||||
"softwareVersionExternalIdentifiers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"vendorId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"drmVersionNumber": {
|
||||
"type": "integer"
|
||||
},
|
||||
"versionRestrictions": {
|
||||
"type": "integer"
|
||||
},
|
||||
"storeCohort": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasOrEverHasHadIAP": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"MacUIRequiredDeviceCapabilities",
|
||||
"UIRequiredDeviceCapabilities",
|
||||
"WKRunsIndependentlyOfCompanionApp",
|
||||
"WKWatchOnly",
|
||||
"appleWatchEnabled",
|
||||
"artistId",
|
||||
"artistName",
|
||||
"bundleDisplayName",
|
||||
"bundleShortVersionString",
|
||||
"bundleVersion",
|
||||
"copyright",
|
||||
"fileExtension",
|
||||
"gameCenterEnabled",
|
||||
"gameCenterEverEnabled",
|
||||
"genre",
|
||||
"genreId",
|
||||
"itemId",
|
||||
"itemName",
|
||||
"kind",
|
||||
"nameTranscriptions",
|
||||
"playlistName",
|
||||
"product-type",
|
||||
"rating",
|
||||
"releaseDate",
|
||||
"requiresRosetta",
|
||||
"runsOnAppleSilicon",
|
||||
"runsOnIntel",
|
||||
"s",
|
||||
"software-platform",
|
||||
"softwareIcon57x57URL",
|
||||
"softwareIconNeedsShine",
|
||||
"softwareSupportedDeviceIds",
|
||||
"softwareVersionBundleId",
|
||||
"softwareVersionExternalIdentifier",
|
||||
"softwareVersionExternalIdentifiers",
|
||||
"vendorId",
|
||||
"drmVersionNumber",
|
||||
"versionRestrictions",
|
||||
"storeCohort",
|
||||
"hasOrEverHasHadIAP"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"songId",
|
||||
"URL",
|
||||
"downloadKey",
|
||||
"artworkURL",
|
||||
"artwork-urls",
|
||||
"md5",
|
||||
"chunks",
|
||||
"isStreamable",
|
||||
"uncompressedSize",
|
||||
"sinfs",
|
||||
"purchaseDate",
|
||||
"download-id",
|
||||
"is-in-queue",
|
||||
"asset-info",
|
||||
"metadata"
|
||||
]
|
||||
}
|
||||
},
|
||||
"download-queue-info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"download-queue-item-count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dsid": {
|
||||
"type": "integer"
|
||||
},
|
||||
"is-auto-download-machine": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"download-queue-item-count",
|
||||
"dsid",
|
||||
"is-auto-download-machine"
|
||||
]
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemIds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"price": {
|
||||
"type": "integer"
|
||||
},
|
||||
"priceType": {
|
||||
"type": "string"
|
||||
},
|
||||
"productTypes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"mtApp": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtClientId": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtEventTime": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtPageId": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtPageType": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtPrevPage": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtRequestId": {
|
||||
"type": "string"
|
||||
},
|
||||
"mtTopic": {
|
||||
"type": "string"
|
||||
},
|
||||
"currency": {
|
||||
"type": "string"
|
||||
},
|
||||
"exchangeRateToUSD": {
|
||||
"type": "number"
|
||||
},
|
||||
"commerceEvent_purchase_priceType": {
|
||||
"type": "string"
|
||||
},
|
||||
"commerceEvent_storeFrontId": {
|
||||
"type": "string"
|
||||
},
|
||||
"commerceEvent_result_resultType": {
|
||||
"type": "integer"
|
||||
},
|
||||
"commerceEvent_flowType": {
|
||||
"type": "integer"
|
||||
},
|
||||
"commerceEvent_flowStep": {
|
||||
"type": "integer"
|
||||
},
|
||||
|
||||
"dialogId": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"messageCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"actionUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"asnState": {
|
||||
"type": "integer"
|
||||
},
|
||||
"eventType": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"mtApp",
|
||||
"mtClientId",
|
||||
"mtEventTime",
|
||||
"mtPageId",
|
||||
"mtPageType",
|
||||
"mtPrevPage",
|
||||
"mtRequestId",
|
||||
"mtTopic"
|
||||
],
|
||||
"optional": [
|
||||
"itemIds",
|
||||
"price",
|
||||
"priceType",
|
||||
"productTypes",
|
||||
"currency",
|
||||
"exchangeRateToUSD",
|
||||
"commerceEvent_purchase_priceType",
|
||||
"commerceEvent_storeFrontId",
|
||||
"commerceEvent_result_resultType",
|
||||
"commerceEvent_flowType",
|
||||
"commerceEvent_flowStep",
|
||||
|
||||
"dialogId",
|
||||
"message",
|
||||
"messageCode",
|
||||
"options",
|
||||
"actionUrl",
|
||||
"asnState",
|
||||
"eventType"
|
||||
]
|
||||
},
|
||||
"duAnonymousPings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"subscriptionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"music": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"isAdmin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isNotEligibleForFreeTrial": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status",
|
||||
"reason",
|
||||
"isAdmin",
|
||||
"isNotEligibleForFreeTrial"
|
||||
]
|
||||
},
|
||||
"terms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"latestTerms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"agreedToTerms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"latestTerms",
|
||||
"agreedToTerms",
|
||||
"source"
|
||||
]
|
||||
}
|
||||
},
|
||||
"account": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isMinor": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"suspectUnderage": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isMinor",
|
||||
"suspectUnderage"
|
||||
]
|
||||
},
|
||||
"family": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hasFamily": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"hasFamily"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"music",
|
||||
"terms",
|
||||
"account",
|
||||
"family"
|
||||
]
|
||||
},
|
||||
"cancel-purchase-batch": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"failureType": {
|
||||
"type": "string"
|
||||
},
|
||||
"customerMessage": {
|
||||
"type": "string"
|
||||
},
|
||||
"m-allowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"dialog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"m-allowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"use-keychain": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isFree": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
"defaultButton": {
|
||||
"type": "string"
|
||||
},
|
||||
"okButtonString": {
|
||||
"type": "string"
|
||||
},
|
||||
"okButtonAction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"buyParams": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemName": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"buyParams",
|
||||
"itemName"
|
||||
]
|
||||
},
|
||||
"cancelButtonString": {
|
||||
"type": "string"
|
||||
},
|
||||
"initialCheckboxValue": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"m-allowed",
|
||||
"use-keychain",
|
||||
"isFree",
|
||||
"message",
|
||||
"explanation",
|
||||
"defaultButton",
|
||||
"okButtonString",
|
||||
"okButtonAction",
|
||||
"cancelButtonString",
|
||||
"initialCheckboxValue"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pings",
|
||||
"metrics"
|
||||
],
|
||||
"optional": [
|
||||
"jingleDocType",
|
||||
"jingleAction",
|
||||
"status",
|
||||
"dsPersonId",
|
||||
"creditDisplay",
|
||||
"creditBalance",
|
||||
"freeSongBalance",
|
||||
"creditDisplayInternal",
|
||||
"authorized",
|
||||
"download-queue-item-count",
|
||||
"songList",
|
||||
"download-queue-info",
|
||||
"duAnonymousPings",
|
||||
"subscriptionStatus",
|
||||
|
||||
"failureType",
|
||||
"customerMessage",
|
||||
"m-allowed",
|
||||
"dialog",
|
||||
"cancel-purchase-batch"
|
||||
]
|
||||
}
|
||||
24
src_mac/ipatool-py/reqs/schemas/schema_defs/store_download_req.json
Executable file
24
src_mac/ipatool-py/reqs/schemas/schema_defs/store_download_req.json
Executable file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"title": "Store Download Req",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creditDisplay": {
|
||||
"type": "string"
|
||||
},
|
||||
"guid": {
|
||||
"type": "string"
|
||||
},
|
||||
"salableAdamId": {
|
||||
"type": "string"
|
||||
},
|
||||
"appExtVrsId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"creditDisplay",
|
||||
"guid",
|
||||
"salableAdamId"
|
||||
]
|
||||
}
|
||||
497
src_mac/ipatool-py/reqs/schemas/schema_defs/store_download_resp.json
Executable file
497
src_mac/ipatool-py/reqs/schemas/schema_defs/store_download_resp.json
Executable file
@@ -0,0 +1,497 @@
|
||||
{
|
||||
"title": "Store Download Resp",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["number","string","boolean","object","array", "null"]
|
||||
}
|
||||
},
|
||||
"cancel-purchase-batch": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"customerMessage": {
|
||||
"type": "string"
|
||||
},
|
||||
"failureType": {
|
||||
"type": "string"
|
||||
},
|
||||
"jingleDocType": {
|
||||
"type": "string"
|
||||
},
|
||||
"jingleAction": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dsPersonId": {
|
||||
"type": "string"
|
||||
},
|
||||
"creditDisplay": {
|
||||
"type": "string"
|
||||
},
|
||||
"creditBalance": {
|
||||
"type": "string"
|
||||
},
|
||||
"freeSongBalance": {
|
||||
"type": "string"
|
||||
},
|
||||
"authorized": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"download-queue-item-count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"songList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"songId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"URL": {
|
||||
"type": "string"
|
||||
},
|
||||
"downloadKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"artworkURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"artwork-urls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image-type": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"image-type",
|
||||
"default"
|
||||
]
|
||||
},
|
||||
"md5": {
|
||||
"type": "string"
|
||||
},
|
||||
"chunks": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chunkSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"hashes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chunkSize",
|
||||
"hashes"
|
||||
]
|
||||
},
|
||||
"isStreamable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"uncompressedSize": {
|
||||
"type": "string"
|
||||
},
|
||||
"sinfs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sinf": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"sinf"
|
||||
]
|
||||
}
|
||||
},
|
||||
"purchaseDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"download-id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is-in-queue": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"asset-info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file-size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"flavor": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file-size",
|
||||
"flavor"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"MacUIRequiredDeviceCapabilities": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"arm64": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"gamekit": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"metal": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"arm64",
|
||||
"gamekit",
|
||||
"metal"
|
||||
]
|
||||
},
|
||||
"UIRequiredDeviceCapabilities": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"arm64": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"gamekit": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"metal": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"arm64",
|
||||
"gamekit",
|
||||
"metal"
|
||||
]
|
||||
},
|
||||
"artistId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"artistName": {
|
||||
"type": "string"
|
||||
},
|
||||
"bundleDisplayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"bundleShortVersionString": {
|
||||
"type": "string"
|
||||
},
|
||||
"bundleVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"copyright": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileExtension": {
|
||||
"type": "string"
|
||||
},
|
||||
"gameCenterEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"gameCenterEverEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"genre": {
|
||||
"type": "string"
|
||||
},
|
||||
"genreId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"itemId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"itemName": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"playlistName": {
|
||||
"type": "string"
|
||||
},
|
||||
"product-type": {
|
||||
"type": "string"
|
||||
},
|
||||
"rating": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"rank": {
|
||||
"type": "integer"
|
||||
},
|
||||
"system": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"content",
|
||||
"label",
|
||||
"rank",
|
||||
"system"
|
||||
]
|
||||
},
|
||||
"releaseDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"requiresRosetta": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"runsOnAppleSilicon": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"runsOnIntel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"s": {
|
||||
"type": "integer"
|
||||
},
|
||||
"software-platform": {
|
||||
"type": "string"
|
||||
},
|
||||
"softwareIcon57x57URL": {
|
||||
"type": "string"
|
||||
},
|
||||
"softwareIconNeedsShine": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"softwareSupportedDeviceIds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"softwareVersionBundleId": {
|
||||
"type": "string"
|
||||
},
|
||||
"softwareVersionExternalIdentifier": {
|
||||
"type": "integer"
|
||||
},
|
||||
"softwareVersionExternalIdentifiers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"subgenres": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"genre": {
|
||||
"type": "string"
|
||||
},
|
||||
"genreId": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"genre",
|
||||
"genreId"
|
||||
]
|
||||
}
|
||||
},
|
||||
"vendorId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"drmVersionNumber": {
|
||||
"type": "integer"
|
||||
},
|
||||
"versionRestrictions": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"MacUIRequiredDeviceCapabilities",
|
||||
"UIRequiredDeviceCapabilities",
|
||||
"artistId",
|
||||
"artistName",
|
||||
"bundleDisplayName",
|
||||
"bundleShortVersionString",
|
||||
"bundleVersion",
|
||||
"copyright",
|
||||
"fileExtension",
|
||||
"gameCenterEnabled",
|
||||
"gameCenterEverEnabled",
|
||||
"genre",
|
||||
"genreId",
|
||||
"itemId",
|
||||
"itemName",
|
||||
"kind",
|
||||
"playlistName",
|
||||
"product-type",
|
||||
"rating",
|
||||
"releaseDate",
|
||||
"requiresRosetta",
|
||||
"runsOnAppleSilicon",
|
||||
"runsOnIntel",
|
||||
"s",
|
||||
"software-platform",
|
||||
"softwareIcon57x57URL",
|
||||
"softwareIconNeedsShine",
|
||||
"softwareSupportedDeviceIds",
|
||||
"softwareVersionBundleId",
|
||||
"softwareVersionExternalIdentifier",
|
||||
"softwareVersionExternalIdentifiers",
|
||||
"subgenres",
|
||||
"vendorId",
|
||||
"drmVersionNumber",
|
||||
"versionRestrictions"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"songId",
|
||||
"URL",
|
||||
"downloadKey",
|
||||
"artworkURL",
|
||||
"artwork-urls",
|
||||
"md5",
|
||||
"chunks",
|
||||
"isStreamable",
|
||||
"uncompressedSize",
|
||||
"sinfs",
|
||||
"purchaseDate",
|
||||
"download-id",
|
||||
"is-in-queue",
|
||||
"asset-info",
|
||||
"metadata"
|
||||
]
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemIds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"currency": {
|
||||
"type": "string"
|
||||
},
|
||||
"exchangeRateToUSD": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"itemIds",
|
||||
"currency",
|
||||
"exchangeRateToUSD"
|
||||
]
|
||||
},
|
||||
"subscriptionStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"terms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"latestTerms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"agreedToTerms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"latestTerms",
|
||||
"agreedToTerms",
|
||||
"source"
|
||||
]
|
||||
}
|
||||
},
|
||||
"account": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isMinor": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"suspectUnderage": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isMinor",
|
||||
"suspectUnderage"
|
||||
]
|
||||
},
|
||||
"family": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hasFamily": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"hasFamily"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"terms",
|
||||
"account",
|
||||
"family"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pings",
|
||||
"jingleDocType",
|
||||
"jingleAction",
|
||||
"status",
|
||||
"dsPersonId",
|
||||
"creditDisplay",
|
||||
"creditBalance",
|
||||
"freeSongBalance",
|
||||
"authorized",
|
||||
"download-queue-item-count",
|
||||
"songList",
|
||||
"metrics",
|
||||
"subscriptionStatus"
|
||||
]
|
||||
}
|
||||
13
src_mac/ipatool-py/reqs/schemas/schema_examples/itunes_lookup_resp.log
Executable file
13
src_mac/ipatool-py/reqs/schemas/schema_examples/itunes_lookup_resp.log
Executable file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"resultCount":1,
|
||||
"results": [
|
||||
{
|
||||
"screenshotUrls":["https://is1-ssl.mzstatic.com/image/thumb/Purple123/v4/c7/9d/5a/c79d5ac9-4543-e9d7-acb3-4753704f1488/pr_source.png/392x696bb.png", "https://is5-ssl.mzstatic.com/image/thumb/Purple123/v4/e4/38/3a/e4383af3-d9ed-3d0c-9c80-ca0d413c0f06/pr_source.png/392x696bb.png", "https://is5-ssl.mzstatic.com/image/thumb/Purple123/v4/08/b2/39/08b23995-3c76-3e74-5af4-3edb4914dc4c/pr_source.png/392x696bb.png"],
|
||||
"ipadScreenshotUrls":["https://is5-ssl.mzstatic.com/image/thumb/Purple113/v4/67/74/39/6774390f-7a12-3d9f-ded7-392b9af90663/pr_source.png/576x768bb.png", "https://is4-ssl.mzstatic.com/image/thumb/Purple123/v4/88/62/85/886285f0-b1c2-ad6b-3ab3-2a5ee70d7c9d/pr_source.png/576x768bb.png", "https://is4-ssl.mzstatic.com/image/thumb/Purple123/v4/29/39/68/29396836-1b94-1561-4bd1-cde929ae5baa/pr_source.png/576x768bb.png"], "appletvScreenshotUrls":[],
|
||||
"artworkUrl60":"https://is3-ssl.mzstatic.com/image/thumb/Purple123/v4/6b/ed/31/6bed31bd-42d1-5ae9-04fd-a04c865af27d/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/60x60bb.jpg",
|
||||
"artworkUrl512":"https://is3-ssl.mzstatic.com/image/thumb/Purple123/v4/6b/ed/31/6bed31bd-42d1-5ae9-04fd-a04c865af27d/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/512x512bb.jpg",
|
||||
"artworkUrl100":"https://is3-ssl.mzstatic.com/image/thumb/Purple123/v4/6b/ed/31/6bed31bd-42d1-5ae9-04fd-a04c865af27d/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/100x100bb.jpg", "artistViewUrl":"https://apps.apple.com/us/developer/potatso-lab-ltd/id1267906737?uo=4",
|
||||
"supportedDevices":["iPhone5s-iPhone5s", "iPadAir-iPadAir", "iPadAirCellular-iPadAirCellular", "iPadMiniRetina-iPadMiniRetina", "iPadMiniRetinaCellular-iPadMiniRetinaCellular", "iPhone6-iPhone6", "iPhone6Plus-iPhone6Plus", "iPadAir2-iPadAir2", "iPadAir2Cellular-iPadAir2Cellular", "iPadMini3-iPadMini3", "iPadMini3Cellular-iPadMini3Cellular", "iPodTouchSixthGen-iPodTouchSixthGen", "iPhone6s-iPhone6s", "iPhone6sPlus-iPhone6sPlus", "iPadMini4-iPadMini4", "iPadMini4Cellular-iPadMini4Cellular", "iPadPro-iPadPro", "iPadProCellular-iPadProCellular", "iPadPro97-iPadPro97", "iPadPro97Cellular-iPadPro97Cellular", "iPhoneSE-iPhoneSE", "iPhone7-iPhone7", "iPhone7Plus-iPhone7Plus", "iPad611-iPad611", "iPad612-iPad612", "iPad71-iPad71", "iPad72-iPad72", "iPad73-iPad73", "iPad74-iPad74", "iPhone8-iPhone8", "iPhone8Plus-iPhone8Plus", "iPhoneX-iPhoneX", "iPad75-iPad75", "iPad76-iPad76", "iPhoneXS-iPhoneXS", "iPhoneXSMax-iPhoneXSMax", "iPhoneXR-iPhoneXR", "iPad812-iPad812", "iPad834-iPad834", "iPad856-iPad856", "iPad878-iPad878", "iPadMini5-iPadMini5", "iPadMini5Cellular-iPadMini5Cellular", "iPadAir3-iPadAir3", "iPadAir3Cellular-iPadAir3Cellular", "iPodTouchSeventhGen-iPodTouchSeventhGen", "iPhone11-iPhone11", "iPhone11Pro-iPhone11Pro", "iPadSeventhGen-iPadSeventhGen", "iPadSeventhGenCellular-iPadSeventhGenCellular", "iPhone11ProMax-iPhone11ProMax", "iPhoneSESecondGen-iPhoneSESecondGen", "iPadProSecondGen-iPadProSecondGen", "iPadProSecondGenCellular-iPadProSecondGenCellular", "iPadProFourthGen-iPadProFourthGen", "iPadProFourthGenCellular-iPadProFourthGenCellular", "iPhone12Mini-iPhone12Mini", "iPhone12-iPhone12", "iPhone12Pro-iPhone12Pro", "iPhone12ProMax-iPhone12ProMax", "iPadAir4-iPadAir4", "iPadAir4Cellular-iPadAir4Cellular", "iPadEighthGen-iPadEighthGen", "iPadEighthGenCellular-iPadEighthGenCellular", "iPadProThirdGen-iPadProThirdGen", "iPadProThirdGenCellular-iPadProThirdGenCellular", "iPadProFifthGen-iPadProFifthGen", "iPadProFifthGenCellular-iPadProFifthGenCellular", "iPhone13Pro-iPhone13Pro", "iPhone13ProMax-iPhone13ProMax", "iPhone13Mini-iPhone13Mini", "iPhone13-iPhone13", "iPadMiniSixthGen-iPadMiniSixthGen", "iPadMiniSixthGenCellular-iPadMiniSixthGenCellular", "iPadNinthGen-iPadNinthGen", "iPadNinthGenCellular-iPadNinthGenCellular", "iPhoneSEThirdGen-iPhoneSEThirdGen", "iPadAirFifthGen-iPadAirFifthGen", "iPadAirFifthGenCellular-iPadAirFifthGenCellular"], "features":["iosUniversal"], "advisories":[], "isGameCenterEnabled":false, "kind":"software", "minimumOsVersion":"13.0", "trackCensoredName":"Potatso Lite", "languageCodesISO2A":["EN", "ZH"], "fileSizeBytes":"18907136", "sellerUrl":"https://potatso.com/en", "formattedPrice":"Free", "contentAdvisoryRating":"4+", "averageUserRatingForCurrentVersion":4.595620000000000260342858382500708103179931640625, "userRatingCountForCurrentVersion":2923, "averageUserRating":4.595620000000000260342858382500708103179931640625, "trackViewUrl":"https://apps.apple.com/us/app/potatso-lite/id1239860606?uo=4", "trackContentRating":"4+", "trackId":1239860606, "trackName":"Potatso Lite", "bundleId":"com.touchingapp.potatsolite", "primaryGenreName":"Utilities", "releaseDate":"2017-06-01T02:34:35Z", "genreIds":["6002", "6007"], "isVppDeviceBasedLicensingEnabled":true, "sellerName":"Potatso Lab LTD", "currentVersionReleaseDate":"2019-12-16T23:27:27Z",
|
||||
"releaseNotes":"With this update, we're bringing you some exciting new features and changes. \n\n=====================\n===== What's New =====\n=====================\n\n• The app will support iOS 13+ from now on.\n• We're introducing the brand new logo with clarity and simplicity.\n• We've redeisnged the whole UI to improve your using experience.\n• Some other internal performance improvements and bug fixes.", "primaryGenreId":6002, "currency":"USD", "version":"2.5.0", "wrapperType":"software", "artistId":1267906737, "artistName":"Potatso Lab LTD", "genres":["Utilities", "Productivity"], "price":0.00,
|
||||
"description":"Potatso Lite is a powerful network tool which empowers your phone to have fully customized network environment. It's friendly for both beginners and power users. \n\nPotatso now supports Shadowsocks, ShadowsocksR, HTTP and Socks5 proxies. You can either setup one by yourself or buy from any proxy providers.\n\nEmbedded smart rouing feature for Chinese users is super helpful which can cost less data in proxy servers and speed up domestic network traffic.\n\n=== Features ===\n- Custom proxy supports Shadowsocks, ShadowsocksR, HTTP and Socks5 \n- Run in the background sustainably without interrupting you\n- Both cellular and Wi-Fi are supported\n- Custom DNS support\n- Smart Routing for Chinese users \n\n=== Privacy ===\nWe respect your privacy so NO confidential data will be uploaded or shared with third parties\n\n=== Feedback ===\nPlease contact on hi@potatso.com", "userRatingCount":2923}]
|
||||
}
|
||||
107
src_mac/ipatool-py/reqs/schemas/schema_examples/store_buyproduct_req.log
Executable file
107
src_mac/ipatool-py/reqs/schemas/schema_examples/store_buyproduct_req.log
Executable file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>appExtVrsId</key>
|
||||
<string>848463733</string>
|
||||
<key>guid</key>
|
||||
<string>22330C8C.C2E39C5C.06C40B91.D0990F95.9A890F9D.AB7F7EB4.56507025</string>
|
||||
<key>kbsync</key>
|
||||
<data>
|
||||
AAQAA5nBc3Q7ETi+TD1x8AM4wbw9sqWglXBaXwuIrlMR9OnAPY89zaoTvbe6PneuS1x1
|
||||
31NxVIqrAsIZLmxy5be8dQtq8je/rtYTRlxduU9NwW4DBcplBx6vs9qhS3Y8B45Zz4T5
|
||||
dkmDG4UnS7xnAPwew7jEX/uY38zZhtKu4IN+sl/Whvyh2SkZg/5vGCtjav17CGbP8ZWo
|
||||
Ci3FhEqAByOL0g6zhPdTHyqF84Apg9fh395tGpzzAWB8mYsRQXJcUHH1cuJjO/qMTkZ4
|
||||
ZxIJqfMaDJpS20nFq+/Bfg9FvC/83AOPnDfXZTsol3PFKqQ6sLgz6dKIho4Qd2UPABnj
|
||||
kBx4TFPeYBlm2T6GKfi8tr+rDhsMrbNczpnaUS+3cesIOvDsE3YCX4isOmMtg5yrJ5pi
|
||||
2GHuofHD2I7Dj6fOh79I7F5OZb71PUvbeABjxvS5b57LGNICSBc/GJ0CEja27kpYals+
|
||||
bgYG0rVm+vqlAHwRpka5jzeK8DLrvTr22vtBLv62LpTVpVVglr5nbk99BcXG5gA=
|
||||
</data>
|
||||
<key>machineName</key>
|
||||
<string>DESKTOP-697LVJS</string>
|
||||
<key>mtApp</key>
|
||||
<string>com.apple.iTunes</string>
|
||||
<key>mtClientId</key>
|
||||
<string>3z21abvCzFDuz5CYz9bdz19maFVKge</string>
|
||||
<key>mtEventTime</key>
|
||||
<string>1652006678827</string>
|
||||
<key>mtPageId</key>
|
||||
<string>ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4</string>
|
||||
<key>mtPageType</key>
|
||||
<string>Search</string>
|
||||
<key>mtPrevPage</key>
|
||||
<string>Purchases</string>
|
||||
<key>mtRequestId</key>
|
||||
<string>3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG</string>
|
||||
<key>mtTopic</key>
|
||||
<string>xp_its_main</string>
|
||||
<key>needDiv</key>
|
||||
<string>0</string>
|
||||
<key>pg</key>
|
||||
<string>default</string>
|
||||
<key>price</key>
|
||||
<string>0</string>
|
||||
<key>pricingParameters</key>
|
||||
<string>STDQ</string>
|
||||
<key>productType</key>
|
||||
<string>C</string>
|
||||
<key>salableAdamId</key>
|
||||
<string>444934666</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ageCheck</key>
|
||||
<string>true</string>
|
||||
<key>appExtVrsId</key>
|
||||
<string>848463733</string>
|
||||
<key>guid</key>
|
||||
<string>22330C8C.C2E39C5C.06C40B91.D0990F95.9A890F9D.AB7F7EB4.56507025</string>
|
||||
<key>hasBeenAuthedForBuy</key>
|
||||
<string>true</string>
|
||||
<key>isInApp</key>
|
||||
<string>false</string>
|
||||
<key>kbsync</key>
|
||||
<data>
|
||||
AAQAA1c+HfGD4vNLZJYBMpBucSo1bxeaeTEZ3FGUKLq0skmzTxVik6IGoQMGaP6OWsJU
|
||||
lBoDb3hxaacD57bkiAgRXc/vr21/CipX55hKLoTE53yah3DwBR9tS1cG7oaHFLIh1Vmn
|
||||
RV7G9LJCQqwSAbr4ugEIVmLULkqaHDfTm8VNDXxYej1p8ghKggMcBT0se5cpDqpVn/bE
|
||||
qehnOl6QsupUNjjLzDm58bmERm/AjGJVBHQveG+4Y+Y4e1eUO6QQcntmypjmSLDqhI60
|
||||
31rCV3zwTTZrHmmXEwsZiGgYlSVHR3ne+O9BE+LIPiQxDwIMvjfV6SrzoOUOLlOKvBsk
|
||||
kI29+6H0QNyMUXojWPQf7bfr+9NBTMgJoDNJd5hEGHqiSKnp9V1ALU8S8QwcYzi3ZDPu
|
||||
A36lMtgMOFEnYibnGnP5S8i2t43ZSBglExE9LGTGO0/IEWX9gEhKjvMDuIwMt9zdtpye
|
||||
CXO8siqHY4MtmLlfwZZc/480ZSHfRF8ZQHBa0J0/pgByNQiNe9KMihz+QN7cQI4=
|
||||
</data>
|
||||
<key>machineName</key>
|
||||
<string>DESKTOP-697LVJS</string>
|
||||
<key>mtApp</key>
|
||||
<string>com.apple.iTunes</string>
|
||||
<key>mtClientId</key>
|
||||
<string>3z21abvCzFDuz5CYz9bdz19maFVKge</string>
|
||||
<key>mtEventTime</key>
|
||||
<string>1652006678827</string>
|
||||
<key>mtPageId</key>
|
||||
<string>ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4</string>
|
||||
<key>mtPageType</key>
|
||||
<string>Search</string>
|
||||
<key>mtPrevPage</key>
|
||||
<string>Purchases</string>
|
||||
<key>mtRequestId</key>
|
||||
<string>3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG</string>
|
||||
<key>mtTopic</key>
|
||||
<string>xp_its_main</string>
|
||||
<key>needDiv</key>
|
||||
<string>0</string>
|
||||
<key>pg</key>
|
||||
<string>default</string>
|
||||
<key>price</key>
|
||||
<string>0</string>
|
||||
<key>pricingParameters</key>
|
||||
<string>STDQ</string>
|
||||
<key>productType</key>
|
||||
<string>C</string>
|
||||
<key>salableAdamId</key>
|
||||
<string>444934666</string>
|
||||
</dict>
|
||||
</plist>
|
||||
561
src_mac/ipatool-py/reqs/schemas/schema_examples/store_buyproduct_resp.log
Executable file
561
src_mac/ipatool-py/reqs/schemas/schema_examples/store_buyproduct_resp.log
Executable file
@@ -0,0 +1,561 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>pings</key>
|
||||
<array><string>https://xp.apple.com/report/2/xp_its_main?app=com.apple.iTunes&code=MZCommerce.ASN.ExpiredPasswordToken&buttons=%E8%8E%B7%E5%8F%96%3A%E5%8F%96%E6%B6%88&baseVersion=1&dsId=16916646015&eventVersion=1&storeFrontHeader=143465-19%2C32&eventTime=1652006682067&eventType=dialog&message=%E9%9C%80%E8%A6%81%E7%99%BB%E5%BD%95</string>
|
||||
</array>
|
||||
<key>metrics</key>
|
||||
<dict>
|
||||
<key>dialogId</key><string>MZCommerce.ASN.ExpiredPasswordToken</string>
|
||||
<key>message</key><string>éè¦ç»å½</string>
|
||||
<key>messageCode</key><string>2072</string>
|
||||
<key>options</key>
|
||||
<array>
|
||||
<string>Get</string>
|
||||
<string>Cancel</string>
|
||||
</array>
|
||||
<key>actionUrl</key><string>p36-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/buyProduct</string>
|
||||
<key>asnState</key><integer>2</integer>
|
||||
<key>mtApp</key><string>com.apple.iTunes</string>
|
||||
<key>mtClientId</key><string>3z21abvCzFDuz5CYz9bdz19maFVKge</string>
|
||||
<key>mtEventTime</key><string>2022-05-08 10:44:38 Etc/GMT</string>
|
||||
<key>mtPageId</key><string>ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4</string>
|
||||
<key>mtPageType</key><string>Search</string>
|
||||
<key>mtPrevPage</key><string>Purchases</string>
|
||||
<key>mtRequestId</key><string>3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG</string>
|
||||
<key>mtTopic</key><string>xp_its_main</string>
|
||||
<key>eventType</key><string>dialog</string>
|
||||
</dict>
|
||||
<key>failureType</key><string>2072</string>
|
||||
<key>customerMessage</key><string>éè¦ç»å½</string>
|
||||
<key>m-allowed</key><true/>
|
||||
<key>dialog</key>
|
||||
<dict><key>kind</key><string>authorization</string>
|
||||
<key>m-allowed</key><true/>
|
||||
<key>use-keychain</key><true/>
|
||||
<key>isFree</key><true/>
|
||||
<key>message</key><string>éè¦ç»å½</string>
|
||||
<key>explanation</key><string>å¦ææ¨æ Apple ID åå¯ç ï¼è¯·å¨æ¤å¤è¾å
¥ãä¾å¦ï¼å¦ææ¨ä½¿ç¨è¿ iTunes Store æ iCloudï¼é£ä¹æ¨å·²æ Apple IDã</string>
|
||||
<key>defaultButton</key><string>ok</string>
|
||||
<key>okButtonString</key><string>è·å</string>
|
||||
<key>okButtonAction</key><dict><key>kind</key><string>Buy</string>
|
||||
<key>buyParams</key><string>mtEventTime=1652006678827&salableAdamId=444934666&mtRequestId=3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG&appExtVrsId=848463733&mtTopic=xp_its_main&guid=22330C8C.C2E39C5C.06C40B91.D0990F95.9A890F9D.AB7F7EB4.56507025&hasBeenAuthedForBuy=true&isInApp=false&price=0&mtClientId=3z21abvCzFDuz5CYz9bdz19maFVKge&productType=C&mtPageType=Search&mtPageId=ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4&machineName=DESKTOP-697LVJS&ageCheck=true&pg=default&mtApp=com.apple.iTunes&needDiv=0&mtPrevPage=Purchases&pricingParameters=STDQ</string>
|
||||
<key>itemName</key><string>QQ</string>
|
||||
</dict>
|
||||
<key>cancelButtonString</key><string>åæ¶</string>
|
||||
<key>initialCheckboxValue</key><true/></dict>
|
||||
<key>cancel-purchase-batch</key><true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>pings</key>
|
||||
<array></array>
|
||||
<key>jingleDocType</key><string>purchaseSuccess</string>
|
||||
<key>jingleAction</key><string>purchaseProduct</string>
|
||||
<key>status</key><integer>0</integer>
|
||||
|
||||
<key>dsPersonId</key><string>10964418715</string>
|
||||
<key>creditDisplay</key><string></string>
|
||||
<key>creditBalance</key><string>1311811</string>
|
||||
<key>freeSongBalance</key><string>1311811</string>
|
||||
|
||||
<key>authorized</key><false/><key>download-queue-item-count</key><integer>0</integer>
|
||||
<key>songList</key>
|
||||
<array>
|
||||
</array>
|
||||
<key>metrics</key>
|
||||
<dict>
|
||||
|
||||
<key>itemIds</key>
|
||||
<array>
|
||||
<integer>580311103</integer>
|
||||
</array>
|
||||
<key>price</key><real>0.00</real>
|
||||
<key>priceType</key><string>STDQ</string>
|
||||
<key>productTypes</key>
|
||||
<array>
|
||||
<string>C</string>
|
||||
</array>
|
||||
<key>currency</key><string>JPY</string>
|
||||
<key>exchangeRateToUSD</key><real>0.0076722418</real>
|
||||
<key>commerceEvent_purchase_priceType</key><string>STDQ</string>
|
||||
<key>commerceEvent_storeFrontId</key><string>143462</string>
|
||||
<key>commerceEvent_result_resultType</key><integer>0</integer>
|
||||
<key>commerceEvent_flowType</key><integer>4</integer>
|
||||
<key>commerceEvent_flowStep</key><integer>6</integer>
|
||||
</dict>
|
||||
|
||||
<key>duAnonymousPings</key>
|
||||
<array>
|
||||
<string>https://xp.apple.com/report/2/xp_app_buy?clientId=0&sf=143462&adamId=580311103</string>
|
||||
</array>
|
||||
<key>subscriptionStatus</key>
|
||||
<dict>
|
||||
<key>terms</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>type</key><string>Store</string>
|
||||
<key>latestTerms</key><integer>28</integer>
|
||||
<key>agreedToTerms</key><integer>31</integer>
|
||||
<key>source</key><string>account</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>account</key>
|
||||
<dict>
|
||||
<key>isMinor</key><false/>
|
||||
<key>suspectUnderage</key><false/>
|
||||
</dict>
|
||||
<key>family</key>
|
||||
<dict>
|
||||
<key>hasFamily</key><false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>pings</key>
|
||||
<array></array>
|
||||
<key>jingleDocType</key><string>purchaseSuccess</string>
|
||||
<key>jingleAction</key><string>purchaseProduct</string>
|
||||
<key>status</key><integer>0</integer>
|
||||
|
||||
<key>dsPersonId</key><string>16916646015</string>
|
||||
<key>creditDisplay</key><string></string>
|
||||
<key>creditBalance</key><string>1311811</string>
|
||||
<key>freeSongBalance</key><string>1311811</string>
|
||||
<key>creditDisplayInternal</key><string>Â¥0.00+0+0+0+0+0</string>
|
||||
|
||||
<key>authorized</key><true/>
|
||||
|
||||
<key>download-queue-item-count</key><integer>1</integer>
|
||||
<key>songList</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>songId</key><integer>444934666</integer>
|
||||
<key>URL</key><string>https://iosapps.itunes.apple.com/itunes-assets/Purple112/v4/8c/5d/34/8c5d343a-2132-8690-c6b1-866ec2f6b2f6/extDirwkazoqouvdywwgjk.lc.14519290919268642.5LAVFF7SAES2W.signed.dpkg.ipa?accessKey=1652201151_1282442801256894451_c%2FtMyevC%2FbCtPTcx3kpjPzNcDfSmgpz1CnzuNFgB%2F1n13VEU1IYDwlq1Xie8WXNHq4U4t341RRlyT3J1OI1Doy1%2FKOG8Pk4u38Kn30NHbCnmgCRC2r9lGlND9ZjU7AxGCQeVb5iHc74Vf5i7Exbg3hq5UfUddWq%2BBe7s3VEyvOX9Rikq0Hzj4OkYUkklrks95yuvrEhXjMFYO8YpQAr0tCjKoyU2rvjS%2BwnPYk5U0Hy6DnusEJ3JmJpJKC6EiLvb</string>
|
||||
<key>downloadKey</key><string></string><key>artworkURL</key><string>https://is5-ssl.mzstatic.com/image/thumb/Purple112/v4/b9/ac/4e/b9ac4ef3-0152-fa92-872f-fe773d799117/AppIcon-1-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/2000x2000bb.jpg</string><key>artwork-urls</key>
|
||||
<dict>
|
||||
<key>image-type</key><string>download-queue-item</string>
|
||||
<key>default</key>
|
||||
<dict>
|
||||
<key>url</key>
|
||||
<string>https://is4-ssl.mzstatic.com/image/thumb/Purple112/v4/b9/ac/4e/b9ac4ef3-0152-fa92-872f-fe773d799117/AppIcon-1-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/57x57bb.jpg</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>md5</key><string>cb3a13a60ac4c9f507b0fefa351e0351</string><key>chunks</key>
|
||||
<dict>
|
||||
<key>chunkSize</key><integer>10485760</integer>
|
||||
<key>hashes</key>
|
||||
<array>
|
||||
<string>a9c3b17b7fd5ce1380580c6f063c469b</string>
|
||||
<string>8dfa7efed3cccca58987ec5fb5aad752</string>
|
||||
<string>c636e4bfcdc17f5ce23a8ef104a44fa3</string>
|
||||
<string>90911e7b4d63bf8d4fc1c0bb2756211b</string>
|
||||
<string>0c7ae0bfa13df1a3cb25d18eccf79083</string>
|
||||
<string>4edbba71a6baabeba209cbe495144017</string>
|
||||
<string>2b915fa08b532ef007787f3cf9ed68e6</string>
|
||||
<string>e8ae486763cd40c730a6f2008bb212df</string>
|
||||
<string>a91b20af08349a115d318a3c1a9ac69c</string>
|
||||
<string>001a0a9e6f0f779365fd1a9e08993afa</string>
|
||||
<string>d9c965a523b445f662aac8eb49fa271e</string>
|
||||
<string>22f61327c32a4569a4d81f29ae93f238</string>
|
||||
<string>3caf910c1da7640a89a51c2ae33e8a3b</string>
|
||||
<string>70fcd6e5517144b1298848bd964f4dcc</string>
|
||||
<string>9fb4924aed5006d2c7037be6532d1652</string>
|
||||
<string>fb341fcaaf6027182f885628e15542f6</string>
|
||||
<string>a0999c33fa684fa21cf2d8c8a4d45e27</string>
|
||||
<string>28025c9522f1dc05360e36a9e6da508b</string>
|
||||
<string>ea980ee5fae29238778cdd16a1f0a2fa</string>
|
||||
<string>699c2c8bcd5b4b9353bba25a58bb45f8</string>
|
||||
<string>10423be39910d01b9e763cc1514fef05</string>
|
||||
<string>3da83694d61846e91a0a21981e88652a</string>
|
||||
<string>937391e158c46e385bf1ebd2f17501aa</string>
|
||||
<string>18a12b305b3a0086d76d4f9c72764a00</string>
|
||||
<string>c19f2b5c1eeb909db77b5d715e023511</string>
|
||||
<string>80552d4ed44472cf0f307bc6e6e4180c</string>
|
||||
<string>9af9bebfb73a15084186032efd64f95b</string>
|
||||
<string>bdc8305c863637d6031c079f3438b2b2</string>
|
||||
<string>406fff31a2d714ffa1eb7c417232c478</string>
|
||||
<string>f29550bd0e718ca8588d13654a14e669</string>
|
||||
<string>ea916c4a8e8f772610bd4de2549ec891</string>
|
||||
<string>9ff8f440fb8260ffe7e39e17215e8e9f</string>
|
||||
<string>b596fe96037d6e0e52401676b87fc6c0</string>
|
||||
<string>1be6f6b3141c80d2b3ad952db9e92ebb</string>
|
||||
<string>4a9061c50a209184f7d4a54f80ce61ef</string>
|
||||
<string>e6505a456ca2273700dcad1dead1f625</string>
|
||||
<string>9ac28b8e7dd43be5fd309f16403afef8</string>
|
||||
<string>5088792cdb4928532714a7023df1275d</string>
|
||||
<string>c0b0d04fd38919c8688c5e793b33d5e4</string>
|
||||
<string>a87331d5e314e6708f6cc67075e36c56</string>
|
||||
<string>63ea3257f99d04b44730924cb4a0d553</string>
|
||||
<string>b3d953d072a3b0e8e264c16d96cebeaf</string>
|
||||
<string>5eff58a53e7f7e87f57fcb038a4ad0a4</string>
|
||||
<string>e37467e9f2c7d336c3742b63ee70b4ab</string>
|
||||
<string>abfcdd0ba248ee9c5bc1b4bf8ab03c15</string>
|
||||
<string>7c4d4ccfcaaef4708f3d1b0fce994294</string>
|
||||
<string>927273c03da6f0fcdc674b40224c0a6d</string>
|
||||
<string>648761edcf4c3d69e147b3671abee7a8</string>
|
||||
<string>146e363283ed83bfd72a8c4d6b8fa7ac</string>
|
||||
<string>69c8e188357d463d00bd7f8134786b1c</string>
|
||||
<string>babbb9b2f0c3d31a4d9e0e0d77852f5e</string>
|
||||
<string>ae46925c45e689783c38f065a9d1e7ea</string>
|
||||
<string>c7f4967cb830f008417c02009ae2196a</string>
|
||||
<string>9b8f17524eb05a512c26bb9e2b89220b</string>
|
||||
<string>11cdc44dd07fe5d9fa3eaba0a179704c</string>
|
||||
<string>d0f17891f1c141e39cfcacfb58c1bb3c</string>
|
||||
<string>3273d004f5971ebee5625868d4978f69</string>
|
||||
<string>0aa895b7d15ad0760156b3062fb6deeb</string>
|
||||
<string>7b27b6e73b0731b307399aa4da837df9</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>isStreamable</key><true/>
|
||||
<key>uncompressedSize</key><integer>795979776</integer>
|
||||
<key>sinfs</key><array> <dict> <key>id</key> <integer>0</integer> <key>sinf</key> <data>AAAEIHNpbmYAAAAMZnJtYWdhbWUAAAAUc2NobQAAAABpdHVuAAAAAAAAA3BzY2hpAAAADHVzZXLwT4h/AAAADGNyZHTenU/fAAAADGFzZHQAAAAAAAAADGtleSAAAAACAAAAGGl2aXaD6ySnLt5l3Pdp75fJ0mjxAAAAWHJpZ2h2ZUlEAAEOnHBsYXQAAAABYXZlcgEBAQB0cmFu3p1P33NpbmcAAAAAc29uZxqFKgp0b29sUDYwNG1lZGkAAACAbW9kZQAAIABoaTMyAAAAAwAAAQhuYW1l5q63IOWmueS7jQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcBwcml2eDOeoQvqIT8I9Y9+tU7xixDGAGJI8TvGWPhI2r7kjf9vKy/JunWkBOLN7Ft3eFaFidNSVs3VTlvYSulf3VT0QAc0+3/pFznoHsNC5pMpQ6y0HdBW+8nG+JbiLjeTU3Nj2akunI45wVAiE2IWr1mERJwFG5CavbGOvHM9JBxkHaczFWUUm60zUHuMiBGZiZVDbVI4Oilxdn0vRb3c2P0zDrhOyxez9B7ppEMzJpFkDa2ieFSE0peNxsx8xi+BLWqQ88IZiCpCrLma3P5Wj40hb0nXclQY2t9Zvnw54A2DC/NHEUEDq1T/cZvAi16AdDRB0/GWOTLf7RNf1R8glahArjoD+fddyk9MuVwEN63YqCH/bNh1L7+hpk0Vh2tDGuEy/KDA30kZgF9Z2FED9LAQpCmx1BHUc60NDq++mCImFNiO528OnwirxjZoMWCZWsyCBL+RDTLcNLZOVDwKJcCqP+xLhbbSD8yoQhug/5reFOIUieODiQHGnfVmUmVe9sDuoJmEME3MHMwUHIpGkVfXVE6SPxA2eoN6EReeKwVwbDeLsp/r/dA8jX7BWW9WSjjfAAAAAAAAAAAAAACIc2lnbggl0CkIwRg6tBbdAWQKT2ZlMkYC2bZvWrsyeLzUy+BSYJAs7v2jr1DkRH/xdJbaDjvmkCvS75EUlCXU0oJYT8BydxZzTpJNMALVfwuJNELOkm8sqlCN1/zP1KM3XOeEMu/k6MmtV4nG3zOtLRcKHg5YRz17WcccdTSz251J2Ugn</data> </dict></array><key>purchaseDate</key><string>2022-05-08T10:45:51Z</string>
|
||||
<key>download-id</key><string>501382282407089488</string>
|
||||
<key>is-in-queue</key><true/>
|
||||
|
||||
<key>asset-info</key>
|
||||
<dict>
|
||||
<key>file-size</key><integer>611092999</integer>
|
||||
<key>flavor</key><string>10:purple</string>
|
||||
</dict>
|
||||
<key>metadata</key><dict>
|
||||
|
||||
<key>MacUIRequiredDeviceCapabilities</key>
|
||||
<dict>
|
||||
<key>arm64</key><true/>
|
||||
</dict>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<dict>
|
||||
<key>arm64</key><true/>
|
||||
</dict>
|
||||
<key>WKRunsIndependentlyOfCompanionApp</key><false/>
|
||||
<key>WKWatchOnly</key><false/>
|
||||
<key>appleWatchEnabled</key><true/>
|
||||
<key>artistId</key><integer>292374531</integer>
|
||||
<key>artistName</key><string>Tencent Technology (Shenzhen) Company Limited</string>
|
||||
<key>bundleDisplayName</key><string>QQ</string>
|
||||
<key>bundleShortVersionString</key><string>8.8.88</string>
|
||||
<key>bundleVersion</key><string>8.8.88.662</string>
|
||||
<key>copyright</key><string>Copyright © 1998- 2022 Tencent. All Rights Reserved</string>
|
||||
<key>fileExtension</key><string>.app</string>
|
||||
<key>gameCenterEnabled</key><false/>
|
||||
<key>gameCenterEverEnabled</key><false/>
|
||||
<key>genre</key><string>社交</string>
|
||||
<key>genreId</key><integer>6005</integer>
|
||||
<key>itemId</key><integer>444934666</integer>
|
||||
<key>itemName</key><string>QQ</string>
|
||||
<key>kind</key><string>software</string>
|
||||
<key>nameTranscriptions</key>
|
||||
<dict>
|
||||
<key>zh-Hans-CN</key>
|
||||
<array>
|
||||
<string>QQ</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>playlistName</key><string>Tencent Technology (Shenzhen) Company Limited</string>
|
||||
<key>product-type</key><string>ios-app</string>
|
||||
<key>rating</key>
|
||||
<dict>
|
||||
<key>content</key><string>å¶å°/轻微çè²æ
å
容æè£¸é² , Advisory.NO.GAMBLING_CONTESTS , å¶å°/轻微çæäººææ§æç¤ºé¢æ , Advisory.NO.UNRESTRICTED_WEB_ACCESS and Advisory.NO.TRUE_GAMBLING</string>
|
||||
<key>label</key><string>12+</string>
|
||||
<key>rank</key><integer>300</integer>
|
||||
<key>system</key><string>itunes-games</string>
|
||||
</dict>
|
||||
<key>releaseDate</key><string>2011-06-23T03:33:55Z</string>
|
||||
<key>requiresRosetta</key><false/>
|
||||
<key>runsOnAppleSilicon</key><true/>
|
||||
<key>runsOnIntel</key><false/>
|
||||
<key>s</key><integer>143465</integer>
|
||||
<key>software-platform</key><string>ios</string>
|
||||
<key>softwareIcon57x57URL</key><string>https://is4-ssl.mzstatic.com/image/thumb/Purple112/v4/b9/ac/4e/b9ac4ef3-0152-fa92-872f-fe773d799117/AppIcon-1-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/114x114bb.jpg</string>
|
||||
<key>softwareIconNeedsShine</key><false/>
|
||||
<key>softwareSupportedDeviceIds</key>
|
||||
<array>
|
||||
<integer>2</integer>
|
||||
<integer>9</integer>
|
||||
<integer>4</integer>
|
||||
</array>
|
||||
<key>softwareVersionBundleId</key><string>com.tencent.mqq</string>
|
||||
<key>softwareVersionExternalIdentifier</key><integer>848463733</integer>
|
||||
<key>softwareVersionExternalIdentifiers</key>
|
||||
<array>
|
||||
<integer>3843900</integer>
|
||||
<integer>3876776</integer>
|
||||
<integer>3941034</integer>
|
||||
<integer>3973775</integer>
|
||||
<integer>4070873</integer>
|
||||
<integer>4135846</integer>
|
||||
<integer>4321059</integer>
|
||||
<integer>4492645</integer>
|
||||
<integer>4917185</integer>
|
||||
<integer>5632593</integer>
|
||||
<integer>6232232</integer>
|
||||
<integer>6860432</integer>
|
||||
<integer>7792605</integer>
|
||||
<integer>9642362</integer>
|
||||
<integer>11556077</integer>
|
||||
<integer>11818464</integer>
|
||||
<integer>12638046</integer>
|
||||
<integer>13422327</integer>
|
||||
<integer>14959445</integer>
|
||||
<integer>15410008</integer>
|
||||
<integer>15765932</integer>
|
||||
<integer>15854719</integer>
|
||||
<integer>16122679</integer>
|
||||
<integer>31562763</integer>
|
||||
<integer>275882650</integer>
|
||||
<integer>385122645</integer>
|
||||
<integer>580102645</integer>
|
||||
<integer>595393136</integer>
|
||||
<integer>608133076</integer>
|
||||
<integer>629072654</integer>
|
||||
<integer>687502658</integer>
|
||||
<integer>747082669</integer>
|
||||
<integer>811253584</integer>
|
||||
<integer>811445779</integer>
|
||||
<integer>811179715</integer>
|
||||
<integer>811445780</integer>
|
||||
<integer>811590055</integer>
|
||||
<integer>811669050</integer>
|
||||
<integer>812133257</integer>
|
||||
<integer>812375519</integer>
|
||||
<integer>812625692</integer>
|
||||
<integer>812972631</integer>
|
||||
<integer>813031156</integer>
|
||||
<integer>813192464</integer>
|
||||
<integer>813298393</integer>
|
||||
<integer>813463229</integer>
|
||||
<integer>813961231</integer>
|
||||
<integer>813962159</integer>
|
||||
<integer>814174262</integer>
|
||||
<integer>814318376</integer>
|
||||
<integer>814527796</integer>
|
||||
<integer>814531991</integer>
|
||||
<integer>814639613</integer>
|
||||
<integer>814754396</integer>
|
||||
<integer>814882132</integer>
|
||||
<integer>815144899</integer>
|
||||
<integer>815147083</integer>
|
||||
<integer>815188393</integer>
|
||||
<integer>815573881</integer>
|
||||
<integer>815574602</integer>
|
||||
<integer>815607136</integer>
|
||||
<integer>815810968</integer>
|
||||
<integer>815897122</integer>
|
||||
<integer>815938087</integer>
|
||||
<integer>815938591</integer>
|
||||
<integer>816130933</integer>
|
||||
<integer>816210673</integer>
|
||||
<integer>816305041</integer>
|
||||
<integer>816356364</integer>
|
||||
<integer>816843896</integer>
|
||||
<integer>816912335</integer>
|
||||
<integer>817028549</integer>
|
||||
<integer>817235792</integer>
|
||||
<integer>817473714</integer>
|
||||
<integer>817549698</integer>
|
||||
<integer>817788181</integer>
|
||||
<integer>817933532</integer>
|
||||
<integer>818110324</integer>
|
||||
<integer>818431910</integer>
|
||||
<integer>818825180</integer>
|
||||
<integer>818979113</integer>
|
||||
<integer>819096686</integer>
|
||||
<integer>819416223</integer>
|
||||
<integer>819489902</integer>
|
||||
<integer>819842838</integer>
|
||||
<integer>819893353</integer>
|
||||
<integer>820113905</integer>
|
||||
<integer>820199943</integer>
|
||||
<integer>820442929</integer>
|
||||
<integer>820548304</integer>
|
||||
<integer>820595060</integer>
|
||||
<integer>821268583</integer>
|
||||
<integer>821341311</integer>
|
||||
<integer>821500924</integer>
|
||||
<integer>821954014</integer>
|
||||
<integer>822037007</integer>
|
||||
<integer>822096329</integer>
|
||||
<integer>822279520</integer>
|
||||
<integer>822523036</integer>
|
||||
<integer>822895308</integer>
|
||||
<integer>822957820</integer>
|
||||
<integer>823194852</integer>
|
||||
<integer>823309872</integer>
|
||||
<integer>823713346</integer>
|
||||
<integer>824097583</integer>
|
||||
<integer>824171129</integer>
|
||||
<integer>824301257</integer>
|
||||
<integer>824389600</integer>
|
||||
<integer>825024981</integer>
|
||||
<integer>825145808</integer>
|
||||
<integer>825307653</integer>
|
||||
<integer>825347730</integer>
|
||||
<integer>825611268</integer>
|
||||
<integer>825729315</integer>
|
||||
<integer>825895542</integer>
|
||||
<integer>825933124</integer>
|
||||
<integer>826313718</integer>
|
||||
<integer>826632543</integer>
|
||||
<integer>826837026</integer>
|
||||
<integer>827460275</integer>
|
||||
<integer>828106847</integer>
|
||||
<integer>828385681</integer>
|
||||
<integer>828600919</integer>
|
||||
<integer>828666943</integer>
|
||||
<integer>828716670</integer>
|
||||
<integer>828897691</integer>
|
||||
<integer>829301009</integer>
|
||||
<integer>829496800</integer>
|
||||
<integer>829679760</integer>
|
||||
<integer>829821912</integer>
|
||||
<integer>830133231</integer>
|
||||
<integer>830530856</integer>
|
||||
<integer>830742895</integer>
|
||||
<integer>831337375</integer>
|
||||
<integer>831405629</integer>
|
||||
<integer>831472755</integer>
|
||||
<integer>831824011</integer>
|
||||
<integer>832139548</integer>
|
||||
<integer>832542612</integer>
|
||||
<integer>832827329</integer>
|
||||
<integer>833393416</integer>
|
||||
<integer>833855517</integer>
|
||||
<integer>834104017</integer>
|
||||
<integer>834138755</integer>
|
||||
<integer>834768091</integer>
|
||||
<integer>834880993</integer>
|
||||
<integer>834939578</integer>
|
||||
<integer>835135459</integer>
|
||||
<integer>835524672</integer>
|
||||
<integer>835716180</integer>
|
||||
<integer>835976856</integer>
|
||||
<integer>836375483</integer>
|
||||
<integer>836825545</integer>
|
||||
<integer>836945925</integer>
|
||||
<integer>837334930</integer>
|
||||
<integer>837735298</integer>
|
||||
<integer>837835768</integer>
|
||||
<integer>837881604</integer>
|
||||
<integer>838238560</integer>
|
||||
<integer>839235987</integer>
|
||||
<integer>839700113</integer>
|
||||
<integer>840003771</integer>
|
||||
<integer>840041841</integer>
|
||||
<integer>840921423</integer>
|
||||
<integer>841227891</integer>
|
||||
<integer>841968948</integer>
|
||||
<integer>842099251</integer>
|
||||
<integer>842166600</integer>
|
||||
<integer>842303496</integer>
|
||||
<integer>842463280</integer>
|
||||
<integer>842670770</integer>
|
||||
<integer>842892434</integer>
|
||||
<integer>843105491</integer>
|
||||
<integer>843416604</integer>
|
||||
<integer>843638409</integer>
|
||||
<integer>843881653</integer>
|
||||
<integer>844115468</integer>
|
||||
<integer>844170367</integer>
|
||||
<integer>844356613</integer>
|
||||
<integer>844638728</integer>
|
||||
<integer>844786323</integer>
|
||||
<integer>844986204</integer>
|
||||
<integer>845375859</integer>
|
||||
<integer>846332674</integer>
|
||||
<integer>846748317</integer>
|
||||
<integer>847332305</integer>
|
||||
<integer>847747163</integer>
|
||||
<integer>848017318</integer>
|
||||
<integer>848463733</integer>
|
||||
</array>
|
||||
<key>vendorId</key><integer>69276</integer>
|
||||
|
||||
<key>drmVersionNumber</key><integer>0</integer>
|
||||
<key>versionRestrictions</key><integer>16843008</integer>
|
||||
<key>storeCohort</key><string>10|date=1652005800000&sf=143465&pgtp=Search&pgid=ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4&prpg=Purchases</string>
|
||||
<key>hasOrEverHasHadIAP</key><true/>
|
||||
</dict>
|
||||
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<key>download-queue-info</key>
|
||||
<dict>
|
||||
<key>download-queue-item-count</key><integer>0</integer>
|
||||
<key>dsid</key><integer>16916646015</integer>
|
||||
<key>is-auto-download-machine</key><false/>
|
||||
|
||||
</dict>
|
||||
<key>metrics</key>
|
||||
<dict>
|
||||
|
||||
<key>itemIds</key>
|
||||
<array>
|
||||
<integer>444934666</integer>
|
||||
</array>
|
||||
<key>price</key><real>0.00</real>
|
||||
<key>priceType</key><string>STDQ</string>
|
||||
<key>productTypes</key>
|
||||
<array>
|
||||
<string>C</string>
|
||||
</array>
|
||||
<key>mtApp</key><string>com.apple.iTunes</string>
|
||||
<key>mtClientId</key><string>3z21abvCzFDuz5CYz9bdz19maFVKge</string>
|
||||
<key>mtEventTime</key><string>2022-05-08 10:44:38 Etc/GMT</string>
|
||||
<key>mtPageId</key><string>ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4</string>
|
||||
<key>mtPageType</key><string>Search</string>
|
||||
<key>mtPrevPage</key><string>Purchases</string>
|
||||
<key>mtRequestId</key><string>3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG</string>
|
||||
<key>mtTopic</key><string>xp_its_main</string>
|
||||
<key>currency</key><string>CNY</string>
|
||||
<key>exchangeRateToUSD</key><real>0.1490268546</real>
|
||||
<key>commerceEvent_purchase_priceType</key><string>STDQ</string>
|
||||
<key>commerceEvent_storeFrontId</key><string>143465</string>
|
||||
<key>commerceEvent_result_resultType</key><integer>0</integer>
|
||||
<key>commerceEvent_flowType</key><integer>4</integer>
|
||||
<key>commerceEvent_flowStep</key><integer>6</integer>
|
||||
</dict>
|
||||
|
||||
<key>duAnonymousPings</key>
|
||||
<array>
|
||||
<string>https://xp.apple.com/report/2/xp_app_buy?clientId=0&sf=143465&adamId=444934666</string>
|
||||
</array>
|
||||
<key>subscriptionStatus</key>
|
||||
<dict>
|
||||
<key>music</key>
|
||||
<dict>
|
||||
<key>status</key><string>Disabled</string>
|
||||
<key>reason</key><string></string>
|
||||
<key>isAdmin</key><false/>
|
||||
<key>isNotEligibleForFreeTrial</key><false/>
|
||||
</dict>
|
||||
<key>terms</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>type</key><string>Store</string>
|
||||
<key>latestTerms</key><integer>22</integer>
|
||||
<key>agreedToTerms</key><integer>22</integer>
|
||||
<key>source</key><string>account</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>account</key>
|
||||
<dict>
|
||||
<key>isMinor</key><false/>
|
||||
<key>suspectUnderage</key><false/>
|
||||
</dict>
|
||||
<key>family</key>
|
||||
<dict>
|
||||
<key>hasFamily</key><false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
250
src_mac/ipatool-py/reqs/schemas/store_authenticate_req.py
Executable file
250
src_mac/ipatool-py/reqs/schemas/store_authenticate_req.py
Executable file
@@ -0,0 +1,250 @@
|
||||
from reprlib import repr as limitedRepr
|
||||
|
||||
|
||||
class StoreAuthenticateReq:
|
||||
|
||||
_types_map = {
|
||||
"appleId": {"type": str, "subtype": None},
|
||||
"attempt": {"type": str, "subtype": None},
|
||||
"createSession": {"type": str, "subtype": None},
|
||||
"guid": {"type": str, "subtype": None},
|
||||
"password": {"type": str, "subtype": None},
|
||||
"rmp": {"type": str, "subtype": None},
|
||||
"why": {"type": str, "subtype": None},
|
||||
}
|
||||
_formats_map = {}
|
||||
_validations_map = {
|
||||
"appleId": {
|
||||
"required": True,
|
||||
},
|
||||
"attempt": {
|
||||
"required": True,
|
||||
},
|
||||
"createSession": {
|
||||
"required": True,
|
||||
},
|
||||
"guid": {
|
||||
"required": True,
|
||||
},
|
||||
"password": {
|
||||
"required": True,
|
||||
},
|
||||
"rmp": {
|
||||
"required": True,
|
||||
},
|
||||
"why": {
|
||||
"required": True,
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
appleId: str = None,
|
||||
attempt: str = None,
|
||||
createSession: str = None,
|
||||
guid: str = None,
|
||||
password: str = None,
|
||||
rmp: str = None,
|
||||
why: str = None,
|
||||
):
|
||||
pass
|
||||
self.__appleId = appleId
|
||||
self.__attempt = attempt
|
||||
self.__createSession = createSession
|
||||
self.__guid = guid
|
||||
self.__password = password
|
||||
self.__rmp = rmp
|
||||
self.__why = why
|
||||
|
||||
def _get_appleId(self):
|
||||
return self.__appleId
|
||||
|
||||
def _set_appleId(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("appleId must be str")
|
||||
|
||||
self.__appleId = value
|
||||
|
||||
appleId = property(_get_appleId, _set_appleId)
|
||||
|
||||
def _get_attempt(self):
|
||||
return self.__attempt
|
||||
|
||||
def _set_attempt(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("attempt must be str")
|
||||
|
||||
self.__attempt = value
|
||||
|
||||
attempt = property(_get_attempt, _set_attempt)
|
||||
|
||||
def _get_createSession(self):
|
||||
return self.__createSession
|
||||
|
||||
def _set_createSession(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("createSession must be str")
|
||||
|
||||
self.__createSession = value
|
||||
|
||||
createSession = property(_get_createSession, _set_createSession)
|
||||
|
||||
def _get_guid(self):
|
||||
return self.__guid
|
||||
|
||||
def _set_guid(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("guid must be str")
|
||||
|
||||
self.__guid = value
|
||||
|
||||
guid = property(_get_guid, _set_guid)
|
||||
|
||||
def _get_password(self):
|
||||
return self.__password
|
||||
|
||||
def _set_password(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("password must be str")
|
||||
|
||||
self.__password = value
|
||||
|
||||
password = property(_get_password, _set_password)
|
||||
|
||||
def _get_rmp(self):
|
||||
return self.__rmp
|
||||
|
||||
def _set_rmp(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("rmp must be str")
|
||||
|
||||
self.__rmp = value
|
||||
|
||||
rmp = property(_get_rmp, _set_rmp)
|
||||
|
||||
def _get_why(self):
|
||||
return self.__why
|
||||
|
||||
def _set_why(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("why must be str")
|
||||
|
||||
self.__why = value
|
||||
|
||||
why = property(_get_why, _set_why)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d):
|
||||
v = {}
|
||||
if "appleId" in d:
|
||||
v["appleId"] = (
|
||||
str.from_dict(d["appleId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["appleId"]
|
||||
)
|
||||
if "attempt" in d:
|
||||
v["attempt"] = (
|
||||
str.from_dict(d["attempt"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["attempt"]
|
||||
)
|
||||
if "createSession" in d:
|
||||
v["createSession"] = (
|
||||
str.from_dict(d["createSession"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["createSession"]
|
||||
)
|
||||
if "guid" in d:
|
||||
v["guid"] = (
|
||||
str.from_dict(d["guid"]) if hasattr(str, "from_dict") else d["guid"]
|
||||
)
|
||||
if "password" in d:
|
||||
v["password"] = (
|
||||
str.from_dict(d["password"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["password"]
|
||||
)
|
||||
if "rmp" in d:
|
||||
v["rmp"] = (
|
||||
str.from_dict(d["rmp"]) if hasattr(str, "from_dict") else d["rmp"]
|
||||
)
|
||||
if "why" in d:
|
||||
v["why"] = (
|
||||
str.from_dict(d["why"]) if hasattr(str, "from_dict") else d["why"]
|
||||
)
|
||||
return StoreAuthenticateReq(**v)
|
||||
|
||||
def as_dict(self):
|
||||
d = {}
|
||||
if self.__appleId is not None:
|
||||
d["appleId"] = (
|
||||
self.__appleId.as_dict()
|
||||
if hasattr(self.__appleId, "as_dict")
|
||||
else self.__appleId
|
||||
)
|
||||
if self.__attempt is not None:
|
||||
d["attempt"] = (
|
||||
self.__attempt.as_dict()
|
||||
if hasattr(self.__attempt, "as_dict")
|
||||
else self.__attempt
|
||||
)
|
||||
if self.__createSession is not None:
|
||||
d["createSession"] = (
|
||||
self.__createSession.as_dict()
|
||||
if hasattr(self.__createSession, "as_dict")
|
||||
else self.__createSession
|
||||
)
|
||||
if self.__guid is not None:
|
||||
d["guid"] = (
|
||||
self.__guid.as_dict()
|
||||
if hasattr(self.__guid, "as_dict")
|
||||
else self.__guid
|
||||
)
|
||||
if self.__password is not None:
|
||||
d["password"] = (
|
||||
self.__password.as_dict()
|
||||
if hasattr(self.__password, "as_dict")
|
||||
else self.__password
|
||||
)
|
||||
if self.__rmp is not None:
|
||||
d["rmp"] = (
|
||||
self.__rmp.as_dict() if hasattr(self.__rmp, "as_dict") else self.__rmp
|
||||
)
|
||||
if self.__why is not None:
|
||||
d["why"] = (
|
||||
self.__why.as_dict() if hasattr(self.__why, "as_dict") else self.__why
|
||||
)
|
||||
return d
|
||||
|
||||
def __repr__(self):
|
||||
return "<Class StoreAuthenticateReq. appleId: {}, attempt: {}, createSession: {}, guid: {}, password: {}, rmp: {}, why: {}>".format(
|
||||
limitedRepr(
|
||||
self.__appleId[:20]
|
||||
if isinstance(self.__appleId, bytes)
|
||||
else self.__appleId
|
||||
),
|
||||
limitedRepr(
|
||||
self.__attempt[:20]
|
||||
if isinstance(self.__attempt, bytes)
|
||||
else self.__attempt
|
||||
),
|
||||
limitedRepr(
|
||||
self.__createSession[:20]
|
||||
if isinstance(self.__createSession, bytes)
|
||||
else self.__createSession
|
||||
),
|
||||
limitedRepr(
|
||||
self.__guid[:20] if isinstance(self.__guid, bytes) else self.__guid
|
||||
),
|
||||
limitedRepr(
|
||||
self.__password[:20]
|
||||
if isinstance(self.__password, bytes)
|
||||
else self.__password
|
||||
),
|
||||
limitedRepr(
|
||||
self.__rmp[:20] if isinstance(self.__rmp, bytes) else self.__rmp
|
||||
),
|
||||
limitedRepr(
|
||||
self.__why[:20] if isinstance(self.__why, bytes) else self.__why
|
||||
),
|
||||
)
|
||||
2638
src_mac/ipatool-py/reqs/schemas/store_authenticate_resp.py
Executable file
2638
src_mac/ipatool-py/reqs/schemas/store_authenticate_resp.py
Executable file
File diff suppressed because it is too large
Load Diff
956
src_mac/ipatool-py/reqs/schemas/store_buyproduct_req.py
Executable file
956
src_mac/ipatool-py/reqs/schemas/store_buyproduct_req.py
Executable file
@@ -0,0 +1,956 @@
|
||||
from reprlib import repr as limitedRepr
|
||||
|
||||
|
||||
class StoreBuyproductReq:
|
||||
|
||||
_types_map = {
|
||||
"ageCheck": {"type": str, "subtype": None},
|
||||
"appExtVrsId": {"type": str, "subtype": None},
|
||||
"guid": {"type": str, "subtype": None},
|
||||
"hasBeenAuthedForBuy": {"type": str, "subtype": None},
|
||||
"isInApp": {"type": str, "subtype": None},
|
||||
"kbsync": {"type": str, "subtype": None},
|
||||
"sbsync": {"type": str, "subtype": None},
|
||||
"afds": {"type": str, "subtype": None},
|
||||
"machineName": {"type": str, "subtype": None},
|
||||
"mtApp": {"type": str, "subtype": None},
|
||||
"mtClientId": {"type": str, "subtype": None},
|
||||
"mtEventTime": {"type": str, "subtype": None},
|
||||
"mtPageId": {"type": str, "subtype": None},
|
||||
"mtPageType": {"type": str, "subtype": None},
|
||||
"mtPrevPage": {"type": str, "subtype": None},
|
||||
"mtRequestId": {"type": str, "subtype": None},
|
||||
"mtTopic": {"type": str, "subtype": None},
|
||||
"needDiv": {"type": str, "subtype": None},
|
||||
"pg": {"type": str, "subtype": None},
|
||||
"price": {"type": str, "subtype": None},
|
||||
"pricingParameters": {"type": str, "subtype": None},
|
||||
"productType": {"type": str, "subtype": None},
|
||||
"salableAdamId": {"type": str, "subtype": None},
|
||||
"hasAskedToFulfillPreorder": {"type": str, "subtype": None},
|
||||
"buyWithoutAuthorization": {"type": str, "subtype": None},
|
||||
"hasDoneAgeCheck": {"type": str, "subtype": None},
|
||||
"hasConfirmedPaymentSheet": {"type": str, "subtype": None},
|
||||
"asn": {"type": str, "subtype": None},
|
||||
}
|
||||
_formats_map = {}
|
||||
_validations_map = {
|
||||
"ageCheck": {
|
||||
"required": False,
|
||||
},
|
||||
"appExtVrsId": {
|
||||
"required": True,
|
||||
},
|
||||
"guid": {
|
||||
"required": True,
|
||||
},
|
||||
"hasBeenAuthedForBuy": {
|
||||
"required": False,
|
||||
},
|
||||
"isInApp": {
|
||||
"required": False,
|
||||
},
|
||||
"kbsync": {
|
||||
"required": True,
|
||||
},
|
||||
"sbsync": {
|
||||
"required": False,
|
||||
},
|
||||
"afds": {
|
||||
"required": False,
|
||||
},
|
||||
"machineName": {
|
||||
"required": False,
|
||||
},
|
||||
"mtApp": {
|
||||
"required": False,
|
||||
},
|
||||
"mtClientId": {
|
||||
"required": False,
|
||||
},
|
||||
"mtEventTime": {
|
||||
"required": False,
|
||||
},
|
||||
"mtPageId": {
|
||||
"required": False,
|
||||
},
|
||||
"mtPageType": {
|
||||
"required": False,
|
||||
},
|
||||
"mtPrevPage": {
|
||||
"required": False,
|
||||
},
|
||||
"mtRequestId": {
|
||||
"required": False,
|
||||
},
|
||||
"mtTopic": {
|
||||
"required": False,
|
||||
},
|
||||
"needDiv": {
|
||||
"required": False,
|
||||
},
|
||||
"pg": {
|
||||
"required": False,
|
||||
},
|
||||
"price": {
|
||||
"required": True,
|
||||
},
|
||||
"pricingParameters": {
|
||||
"required": True,
|
||||
},
|
||||
"productType": {
|
||||
"required": True,
|
||||
},
|
||||
"salableAdamId": {
|
||||
"required": True,
|
||||
},
|
||||
"hasAskedToFulfillPreorder": {
|
||||
"required": False,
|
||||
},
|
||||
"buyWithoutAuthorization": {
|
||||
"required": False,
|
||||
},
|
||||
"hasDoneAgeCheck": {
|
||||
"required": False,
|
||||
},
|
||||
"hasConfirmedPaymentSheet": {
|
||||
"required": False,
|
||||
},
|
||||
"asn": {
|
||||
"required": False,
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ageCheck: str = None,
|
||||
appExtVrsId: str = None,
|
||||
guid: str = None,
|
||||
hasBeenAuthedForBuy: str = None,
|
||||
isInApp: str = None,
|
||||
kbsync: str = None,
|
||||
sbsync: str = None,
|
||||
afds: str = None,
|
||||
machineName: str = None,
|
||||
mtApp: str = None,
|
||||
mtClientId: str = None,
|
||||
mtEventTime: str = None,
|
||||
mtPageId: str = None,
|
||||
mtPageType: str = None,
|
||||
mtPrevPage: str = None,
|
||||
mtRequestId: str = None,
|
||||
mtTopic: str = None,
|
||||
needDiv: str = None,
|
||||
pg: str = None,
|
||||
price: str = None,
|
||||
pricingParameters: str = None,
|
||||
productType: str = None,
|
||||
salableAdamId: str = None,
|
||||
hasAskedToFulfillPreorder: str = None,
|
||||
buyWithoutAuthorization: str = None,
|
||||
hasDoneAgeCheck: str = None,
|
||||
hasConfirmedPaymentSheet: str = None,
|
||||
asn: str = None,
|
||||
):
|
||||
pass
|
||||
self.__ageCheck = ageCheck
|
||||
self.__appExtVrsId = appExtVrsId
|
||||
self.__guid = guid
|
||||
self.__hasBeenAuthedForBuy = hasBeenAuthedForBuy
|
||||
self.__isInApp = isInApp
|
||||
self.__kbsync = kbsync
|
||||
self.__sbsync = sbsync
|
||||
self.__afds = afds
|
||||
self.__machineName = machineName
|
||||
self.__mtApp = mtApp
|
||||
self.__mtClientId = mtClientId
|
||||
self.__mtEventTime = mtEventTime
|
||||
self.__mtPageId = mtPageId
|
||||
self.__mtPageType = mtPageType
|
||||
self.__mtPrevPage = mtPrevPage
|
||||
self.__mtRequestId = mtRequestId
|
||||
self.__mtTopic = mtTopic
|
||||
self.__needDiv = needDiv
|
||||
self.__pg = pg
|
||||
self.__price = price
|
||||
self.__pricingParameters = pricingParameters
|
||||
self.__productType = productType
|
||||
self.__salableAdamId = salableAdamId
|
||||
self.__hasAskedToFulfillPreorder = hasAskedToFulfillPreorder
|
||||
self.__buyWithoutAuthorization = buyWithoutAuthorization
|
||||
self.__hasDoneAgeCheck = hasDoneAgeCheck
|
||||
self.__hasConfirmedPaymentSheet = hasConfirmedPaymentSheet
|
||||
self.__asn = asn
|
||||
|
||||
def _get_ageCheck(self):
|
||||
return self.__ageCheck
|
||||
|
||||
def _set_ageCheck(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("ageCheck must be str")
|
||||
|
||||
self.__ageCheck = value
|
||||
|
||||
ageCheck = property(_get_ageCheck, _set_ageCheck)
|
||||
|
||||
def _get_appExtVrsId(self):
|
||||
return self.__appExtVrsId
|
||||
|
||||
def _set_appExtVrsId(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("appExtVrsId must be str")
|
||||
|
||||
self.__appExtVrsId = value
|
||||
|
||||
appExtVrsId = property(_get_appExtVrsId, _set_appExtVrsId)
|
||||
|
||||
def _get_guid(self):
|
||||
return self.__guid
|
||||
|
||||
def _set_guid(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("guid must be str")
|
||||
|
||||
self.__guid = value
|
||||
|
||||
guid = property(_get_guid, _set_guid)
|
||||
|
||||
def _get_hasBeenAuthedForBuy(self):
|
||||
return self.__hasBeenAuthedForBuy
|
||||
|
||||
def _set_hasBeenAuthedForBuy(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("hasBeenAuthedForBuy must be str")
|
||||
|
||||
self.__hasBeenAuthedForBuy = value
|
||||
|
||||
hasBeenAuthedForBuy = property(_get_hasBeenAuthedForBuy, _set_hasBeenAuthedForBuy)
|
||||
|
||||
def _get_isInApp(self):
|
||||
return self.__isInApp
|
||||
|
||||
def _set_isInApp(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("isInApp must be str")
|
||||
|
||||
self.__isInApp = value
|
||||
|
||||
isInApp = property(_get_isInApp, _set_isInApp)
|
||||
|
||||
def _get_kbsync(self):
|
||||
return self.__kbsync
|
||||
|
||||
def _set_kbsync(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("kbsync must be str")
|
||||
|
||||
self.__kbsync = value
|
||||
|
||||
kbsync = property(_get_kbsync, _set_kbsync)
|
||||
|
||||
def _get_sbsync(self):
|
||||
return self.__sbsync
|
||||
|
||||
def _set_sbsync(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("sbsync must be str")
|
||||
|
||||
self.__sbsync = value
|
||||
|
||||
sbsync = property(_get_sbsync, _set_sbsync)
|
||||
|
||||
def _get_afds(self):
|
||||
return self.__afds
|
||||
|
||||
def _set_afds(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("afds must be str")
|
||||
|
||||
self.__afds = value
|
||||
|
||||
afds = property(_get_afds, _set_afds)
|
||||
|
||||
def _get_machineName(self):
|
||||
return self.__machineName
|
||||
|
||||
def _set_machineName(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("machineName must be str")
|
||||
|
||||
self.__machineName = value
|
||||
|
||||
machineName = property(_get_machineName, _set_machineName)
|
||||
|
||||
def _get_mtApp(self):
|
||||
return self.__mtApp
|
||||
|
||||
def _set_mtApp(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtApp must be str")
|
||||
|
||||
self.__mtApp = value
|
||||
|
||||
mtApp = property(_get_mtApp, _set_mtApp)
|
||||
|
||||
def _get_mtClientId(self):
|
||||
return self.__mtClientId
|
||||
|
||||
def _set_mtClientId(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtClientId must be str")
|
||||
|
||||
self.__mtClientId = value
|
||||
|
||||
mtClientId = property(_get_mtClientId, _set_mtClientId)
|
||||
|
||||
def _get_mtEventTime(self):
|
||||
return self.__mtEventTime
|
||||
|
||||
def _set_mtEventTime(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtEventTime must be str")
|
||||
|
||||
self.__mtEventTime = value
|
||||
|
||||
mtEventTime = property(_get_mtEventTime, _set_mtEventTime)
|
||||
|
||||
def _get_mtPageId(self):
|
||||
return self.__mtPageId
|
||||
|
||||
def _set_mtPageId(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtPageId must be str")
|
||||
|
||||
self.__mtPageId = value
|
||||
|
||||
mtPageId = property(_get_mtPageId, _set_mtPageId)
|
||||
|
||||
def _get_mtPageType(self):
|
||||
return self.__mtPageType
|
||||
|
||||
def _set_mtPageType(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtPageType must be str")
|
||||
|
||||
self.__mtPageType = value
|
||||
|
||||
mtPageType = property(_get_mtPageType, _set_mtPageType)
|
||||
|
||||
def _get_mtPrevPage(self):
|
||||
return self.__mtPrevPage
|
||||
|
||||
def _set_mtPrevPage(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtPrevPage must be str")
|
||||
|
||||
self.__mtPrevPage = value
|
||||
|
||||
mtPrevPage = property(_get_mtPrevPage, _set_mtPrevPage)
|
||||
|
||||
def _get_mtRequestId(self):
|
||||
return self.__mtRequestId
|
||||
|
||||
def _set_mtRequestId(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtRequestId must be str")
|
||||
|
||||
self.__mtRequestId = value
|
||||
|
||||
mtRequestId = property(_get_mtRequestId, _set_mtRequestId)
|
||||
|
||||
def _get_mtTopic(self):
|
||||
return self.__mtTopic
|
||||
|
||||
def _set_mtTopic(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("mtTopic must be str")
|
||||
|
||||
self.__mtTopic = value
|
||||
|
||||
mtTopic = property(_get_mtTopic, _set_mtTopic)
|
||||
|
||||
def _get_needDiv(self):
|
||||
return self.__needDiv
|
||||
|
||||
def _set_needDiv(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("needDiv must be str")
|
||||
|
||||
self.__needDiv = value
|
||||
|
||||
needDiv = property(_get_needDiv, _set_needDiv)
|
||||
|
||||
def _get_pg(self):
|
||||
return self.__pg
|
||||
|
||||
def _set_pg(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("pg must be str")
|
||||
|
||||
self.__pg = value
|
||||
|
||||
pg = property(_get_pg, _set_pg)
|
||||
|
||||
def _get_price(self):
|
||||
return self.__price
|
||||
|
||||
def _set_price(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("price must be str")
|
||||
|
||||
self.__price = value
|
||||
|
||||
price = property(_get_price, _set_price)
|
||||
|
||||
def _get_pricingParameters(self):
|
||||
return self.__pricingParameters
|
||||
|
||||
def _set_pricingParameters(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("pricingParameters must be str")
|
||||
|
||||
self.__pricingParameters = value
|
||||
|
||||
pricingParameters = property(_get_pricingParameters, _set_pricingParameters)
|
||||
|
||||
def _get_productType(self):
|
||||
return self.__productType
|
||||
|
||||
def _set_productType(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("productType must be str")
|
||||
|
||||
self.__productType = value
|
||||
|
||||
productType = property(_get_productType, _set_productType)
|
||||
|
||||
def _get_salableAdamId(self):
|
||||
return self.__salableAdamId
|
||||
|
||||
def _set_salableAdamId(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("salableAdamId must be str")
|
||||
|
||||
self.__salableAdamId = value
|
||||
|
||||
salableAdamId = property(_get_salableAdamId, _set_salableAdamId)
|
||||
|
||||
def _get_hasAskedToFulfillPreorder(self):
|
||||
return self.__hasAskedToFulfillPreorder
|
||||
|
||||
def _set_hasAskedToFulfillPreorder(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("hasAskedToFulfillPreorder must be str")
|
||||
|
||||
self.__hasAskedToFulfillPreorder = value
|
||||
|
||||
hasAskedToFulfillPreorder = property(
|
||||
_get_hasAskedToFulfillPreorder, _set_hasAskedToFulfillPreorder
|
||||
)
|
||||
|
||||
def _get_buyWithoutAuthorization(self):
|
||||
return self.__buyWithoutAuthorization
|
||||
|
||||
def _set_buyWithoutAuthorization(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("buyWithoutAuthorization must be str")
|
||||
|
||||
self.__buyWithoutAuthorization = value
|
||||
|
||||
buyWithoutAuthorization = property(
|
||||
_get_buyWithoutAuthorization, _set_buyWithoutAuthorization
|
||||
)
|
||||
|
||||
def _get_hasDoneAgeCheck(self):
|
||||
return self.__hasDoneAgeCheck
|
||||
|
||||
def _set_hasDoneAgeCheck(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("hasDoneAgeCheck must be str")
|
||||
|
||||
self.__hasDoneAgeCheck = value
|
||||
|
||||
hasDoneAgeCheck = property(_get_hasDoneAgeCheck, _set_hasDoneAgeCheck)
|
||||
|
||||
def _get_hasConfirmedPaymentSheet(self):
|
||||
return self.__hasConfirmedPaymentSheet
|
||||
|
||||
def _set_hasConfirmedPaymentSheet(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("hasConfirmedPaymentSheet must be str")
|
||||
|
||||
self.__hasConfirmedPaymentSheet = value
|
||||
|
||||
hasConfirmedPaymentSheet = property(_get_hasConfirmedPaymentSheet, _set_hasConfirmedPaymentSheet)
|
||||
|
||||
def _get_asn(self):
|
||||
return self.__asn
|
||||
|
||||
def _set_asn(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("asn must be str")
|
||||
|
||||
self.__asn = value
|
||||
|
||||
asn = property(_get_asn, _set_asn)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d):
|
||||
v = {}
|
||||
if "ageCheck" in d:
|
||||
v["ageCheck"] = (
|
||||
str.from_dict(d["ageCheck"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["ageCheck"]
|
||||
)
|
||||
if "appExtVrsId" in d:
|
||||
v["appExtVrsId"] = (
|
||||
str.from_dict(d["appExtVrsId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["appExtVrsId"]
|
||||
)
|
||||
if "guid" in d:
|
||||
v["guid"] = (
|
||||
str.from_dict(d["guid"]) if hasattr(str, "from_dict") else d["guid"]
|
||||
)
|
||||
if "hasBeenAuthedForBuy" in d:
|
||||
v["hasBeenAuthedForBuy"] = (
|
||||
str.from_dict(d["hasBeenAuthedForBuy"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["hasBeenAuthedForBuy"]
|
||||
)
|
||||
if "isInApp" in d:
|
||||
v["isInApp"] = (
|
||||
str.from_dict(d["isInApp"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["isInApp"]
|
||||
)
|
||||
if "kbsync" in d:
|
||||
v["kbsync"] = (
|
||||
str.from_dict(d["kbsync"]) if hasattr(str, "from_dict") else d["kbsync"]
|
||||
)
|
||||
if "sbsync" in d:
|
||||
v["sbsync"] = (
|
||||
str.from_dict(d["sbsync"]) if hasattr(str, "from_dict") else d["sbsync"]
|
||||
)
|
||||
if "afds" in d:
|
||||
v["afds"] = (
|
||||
str.from_dict(d["afds"]) if hasattr(str, "from_dict") else d["afds"]
|
||||
)
|
||||
if "machineName" in d:
|
||||
v["machineName"] = (
|
||||
str.from_dict(d["machineName"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["machineName"]
|
||||
)
|
||||
if "mtApp" in d:
|
||||
v["mtApp"] = (
|
||||
str.from_dict(d["mtApp"]) if hasattr(str, "from_dict") else d["mtApp"]
|
||||
)
|
||||
if "mtClientId" in d:
|
||||
v["mtClientId"] = (
|
||||
str.from_dict(d["mtClientId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["mtClientId"]
|
||||
)
|
||||
if "mtEventTime" in d:
|
||||
v["mtEventTime"] = (
|
||||
str.from_dict(d["mtEventTime"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["mtEventTime"]
|
||||
)
|
||||
if "mtPageId" in d:
|
||||
v["mtPageId"] = (
|
||||
str.from_dict(d["mtPageId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["mtPageId"]
|
||||
)
|
||||
if "mtPageType" in d:
|
||||
v["mtPageType"] = (
|
||||
str.from_dict(d["mtPageType"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["mtPageType"]
|
||||
)
|
||||
if "mtPrevPage" in d:
|
||||
v["mtPrevPage"] = (
|
||||
str.from_dict(d["mtPrevPage"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["mtPrevPage"]
|
||||
)
|
||||
if "mtRequestId" in d:
|
||||
v["mtRequestId"] = (
|
||||
str.from_dict(d["mtRequestId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["mtRequestId"]
|
||||
)
|
||||
if "mtTopic" in d:
|
||||
v["mtTopic"] = (
|
||||
str.from_dict(d["mtTopic"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["mtTopic"]
|
||||
)
|
||||
if "needDiv" in d:
|
||||
v["needDiv"] = (
|
||||
str.from_dict(d["needDiv"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["needDiv"]
|
||||
)
|
||||
if "pg" in d:
|
||||
v["pg"] = str.from_dict(d["pg"]) if hasattr(str, "from_dict") else d["pg"]
|
||||
if "price" in d:
|
||||
v["price"] = (
|
||||
str.from_dict(d["price"]) if hasattr(str, "from_dict") else d["price"]
|
||||
)
|
||||
if "pricingParameters" in d:
|
||||
v["pricingParameters"] = (
|
||||
str.from_dict(d["pricingParameters"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["pricingParameters"]
|
||||
)
|
||||
if "productType" in d:
|
||||
v["productType"] = (
|
||||
str.from_dict(d["productType"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["productType"]
|
||||
)
|
||||
if "salableAdamId" in d:
|
||||
v["salableAdamId"] = (
|
||||
str.from_dict(d["salableAdamId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["salableAdamId"]
|
||||
)
|
||||
if "hasAskedToFulfillPreorder" in d:
|
||||
v["hasAskedToFulfillPreorder"] = (
|
||||
str.from_dict(d["hasAskedToFulfillPreorder"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["hasAskedToFulfillPreorder"]
|
||||
)
|
||||
if "buyWithoutAuthorization" in d:
|
||||
v["buyWithoutAuthorization"] = (
|
||||
str.from_dict(d["buyWithoutAuthorization"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["buyWithoutAuthorization"]
|
||||
)
|
||||
if "hasDoneAgeCheck" in d:
|
||||
v["hasDoneAgeCheck"] = (
|
||||
str.from_dict(d["hasDoneAgeCheck"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["hasDoneAgeCheck"]
|
||||
)
|
||||
if "hasConfirmedPaymentSheet" in d:
|
||||
v["hasConfirmedPaymentSheet"] = (
|
||||
str.from_dict(d["hasConfirmedPaymentSheet"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["hasConfirmedPaymentSheet"]
|
||||
)
|
||||
if "asn" in d:
|
||||
v["asn"] = (
|
||||
str.from_dict(d["asn"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["asn"]
|
||||
)
|
||||
return StoreBuyproductReq(**v)
|
||||
|
||||
def as_dict(self):
|
||||
d = {}
|
||||
if self.__ageCheck is not None:
|
||||
d["ageCheck"] = (
|
||||
self.__ageCheck.as_dict()
|
||||
if hasattr(self.__ageCheck, "as_dict")
|
||||
else self.__ageCheck
|
||||
)
|
||||
if self.__appExtVrsId is not None:
|
||||
d["appExtVrsId"] = (
|
||||
self.__appExtVrsId.as_dict()
|
||||
if hasattr(self.__appExtVrsId, "as_dict")
|
||||
else self.__appExtVrsId
|
||||
)
|
||||
if self.__guid is not None:
|
||||
d["guid"] = (
|
||||
self.__guid.as_dict()
|
||||
if hasattr(self.__guid, "as_dict")
|
||||
else self.__guid
|
||||
)
|
||||
if self.__hasBeenAuthedForBuy is not None:
|
||||
d["hasBeenAuthedForBuy"] = (
|
||||
self.__hasBeenAuthedForBuy.as_dict()
|
||||
if hasattr(self.__hasBeenAuthedForBuy, "as_dict")
|
||||
else self.__hasBeenAuthedForBuy
|
||||
)
|
||||
if self.__isInApp is not None:
|
||||
d["isInApp"] = (
|
||||
self.__isInApp.as_dict()
|
||||
if hasattr(self.__isInApp, "as_dict")
|
||||
else self.__isInApp
|
||||
)
|
||||
if self.__kbsync is not None:
|
||||
d["kbsync"] = (
|
||||
self.__kbsync.as_dict()
|
||||
if hasattr(self.__kbsync, "as_dict")
|
||||
else self.__kbsync
|
||||
)
|
||||
if self.__sbsync is not None:
|
||||
d["sbsync"] = (
|
||||
self.__sbsync.as_dict()
|
||||
if hasattr(self.__sbsync, "as_dict")
|
||||
else self.__sbsync
|
||||
)
|
||||
if self.__afds is not None:
|
||||
d["afds"] = (
|
||||
self.__afds.as_dict()
|
||||
if hasattr(self.__afds, "as_dict")
|
||||
else self.__afds
|
||||
)
|
||||
if self.__machineName is not None:
|
||||
d["machineName"] = (
|
||||
self.__machineName.as_dict()
|
||||
if hasattr(self.__machineName, "as_dict")
|
||||
else self.__machineName
|
||||
)
|
||||
if self.__mtApp is not None:
|
||||
d["mtApp"] = (
|
||||
self.__mtApp.as_dict()
|
||||
if hasattr(self.__mtApp, "as_dict")
|
||||
else self.__mtApp
|
||||
)
|
||||
if self.__mtClientId is not None:
|
||||
d["mtClientId"] = (
|
||||
self.__mtClientId.as_dict()
|
||||
if hasattr(self.__mtClientId, "as_dict")
|
||||
else self.__mtClientId
|
||||
)
|
||||
if self.__mtEventTime is not None:
|
||||
d["mtEventTime"] = (
|
||||
self.__mtEventTime.as_dict()
|
||||
if hasattr(self.__mtEventTime, "as_dict")
|
||||
else self.__mtEventTime
|
||||
)
|
||||
if self.__mtPageId is not None:
|
||||
d["mtPageId"] = (
|
||||
self.__mtPageId.as_dict()
|
||||
if hasattr(self.__mtPageId, "as_dict")
|
||||
else self.__mtPageId
|
||||
)
|
||||
if self.__mtPageType is not None:
|
||||
d["mtPageType"] = (
|
||||
self.__mtPageType.as_dict()
|
||||
if hasattr(self.__mtPageType, "as_dict")
|
||||
else self.__mtPageType
|
||||
)
|
||||
if self.__mtPrevPage is not None:
|
||||
d["mtPrevPage"] = (
|
||||
self.__mtPrevPage.as_dict()
|
||||
if hasattr(self.__mtPrevPage, "as_dict")
|
||||
else self.__mtPrevPage
|
||||
)
|
||||
if self.__mtRequestId is not None:
|
||||
d["mtRequestId"] = (
|
||||
self.__mtRequestId.as_dict()
|
||||
if hasattr(self.__mtRequestId, "as_dict")
|
||||
else self.__mtRequestId
|
||||
)
|
||||
if self.__mtTopic is not None:
|
||||
d["mtTopic"] = (
|
||||
self.__mtTopic.as_dict()
|
||||
if hasattr(self.__mtTopic, "as_dict")
|
||||
else self.__mtTopic
|
||||
)
|
||||
if self.__needDiv is not None:
|
||||
d["needDiv"] = (
|
||||
self.__needDiv.as_dict()
|
||||
if hasattr(self.__needDiv, "as_dict")
|
||||
else self.__needDiv
|
||||
)
|
||||
if self.__pg is not None:
|
||||
d["pg"] = (
|
||||
self.__pg.as_dict() if hasattr(self.__pg, "as_dict") else self.__pg
|
||||
)
|
||||
if self.__price is not None:
|
||||
d["price"] = (
|
||||
self.__price.as_dict()
|
||||
if hasattr(self.__price, "as_dict")
|
||||
else self.__price
|
||||
)
|
||||
if self.__pricingParameters is not None:
|
||||
d["pricingParameters"] = (
|
||||
self.__pricingParameters.as_dict()
|
||||
if hasattr(self.__pricingParameters, "as_dict")
|
||||
else self.__pricingParameters
|
||||
)
|
||||
if self.__productType is not None:
|
||||
d["productType"] = (
|
||||
self.__productType.as_dict()
|
||||
if hasattr(self.__productType, "as_dict")
|
||||
else self.__productType
|
||||
)
|
||||
if self.__salableAdamId is not None:
|
||||
d["salableAdamId"] = (
|
||||
self.__salableAdamId.as_dict()
|
||||
if hasattr(self.__salableAdamId, "as_dict")
|
||||
else self.__salableAdamId
|
||||
)
|
||||
if self.__hasAskedToFulfillPreorder is not None:
|
||||
d["hasAskedToFulfillPreorder"] = (
|
||||
self.__hasAskedToFulfillPreorder.as_dict()
|
||||
if hasattr(self.__hasAskedToFulfillPreorder, "as_dict")
|
||||
else self.__hasAskedToFulfillPreorder
|
||||
)
|
||||
if self.__buyWithoutAuthorization is not None:
|
||||
d["buyWithoutAuthorization"] = (
|
||||
self.__buyWithoutAuthorization.as_dict()
|
||||
if hasattr(self.__buyWithoutAuthorization, "as_dict")
|
||||
else self.__buyWithoutAuthorization
|
||||
)
|
||||
if self.__hasDoneAgeCheck is not None:
|
||||
d["hasDoneAgeCheck"] = (
|
||||
self.__hasDoneAgeCheck.as_dict()
|
||||
if hasattr(self.__hasDoneAgeCheck, "as_dict")
|
||||
else self.__hasDoneAgeCheck
|
||||
)
|
||||
if self.__hasConfirmedPaymentSheet is not None:
|
||||
d["hasConfirmedPaymentSheet"] = (
|
||||
self.__hasConfirmedPaymentSheet.as_dict()
|
||||
if hasattr(self.__hasConfirmedPaymentSheet, "as_dict")
|
||||
else self.__hasConfirmedPaymentSheet
|
||||
)
|
||||
if self.__asn is not None:
|
||||
d["asn"] = (
|
||||
self.__asn.as_dict()
|
||||
if hasattr(self.__asn, "as_dict")
|
||||
else self.__asn
|
||||
)
|
||||
return d
|
||||
|
||||
def __repr__(self):
|
||||
return "<Class StoreBuyproductReq. ageCheck: {}, appExtVrsId: {}, guid: {}, hasBeenAuthedForBuy: {}, isInApp: {}, kbsync: {}, sbsync: {}, afds: {}, machineName: {}, mtApp: {}, mtClientId: {}, mtEventTime: {}, mtPageId: {}, mtPageType: {}, mtPrevPage: {}, mtRequestId: {}, mtTopic: {}, needDiv: {}, pg: {}, price: {}, pricingParameters: {}, productType: {}, salableAdamId: {}, hasAskedToFulfillPreorder: {}, buyWithoutAuthorization: {}, hasDoneAgeCheck: {}>".format(
|
||||
limitedRepr(
|
||||
self.__ageCheck[:20]
|
||||
if isinstance(self.__ageCheck, bytes)
|
||||
else self.__ageCheck
|
||||
),
|
||||
limitedRepr(
|
||||
self.__appExtVrsId[:20]
|
||||
if isinstance(self.__appExtVrsId, bytes)
|
||||
else self.__appExtVrsId
|
||||
),
|
||||
limitedRepr(
|
||||
self.__guid[:20] if isinstance(self.__guid, bytes) else self.__guid
|
||||
),
|
||||
limitedRepr(
|
||||
self.__hasBeenAuthedForBuy[:20]
|
||||
if isinstance(self.__hasBeenAuthedForBuy, bytes)
|
||||
else self.__hasBeenAuthedForBuy
|
||||
),
|
||||
limitedRepr(
|
||||
self.__isInApp[:20]
|
||||
if isinstance(self.__isInApp, bytes)
|
||||
else self.__isInApp
|
||||
),
|
||||
limitedRepr(
|
||||
self.__kbsync[:20]
|
||||
if isinstance(self.__kbsync, bytes)
|
||||
else self.__kbsync
|
||||
),
|
||||
limitedRepr(
|
||||
self.__sbsync[:20]
|
||||
if isinstance(self.__sbsync, bytes)
|
||||
else self.__sbsync
|
||||
),
|
||||
limitedRepr(
|
||||
self.__afds[:20]
|
||||
if isinstance(self.__afds, bytes)
|
||||
else self.__afds
|
||||
),
|
||||
limitedRepr(
|
||||
self.__machineName[:20]
|
||||
if isinstance(self.__machineName, bytes)
|
||||
else self.__machineName
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtApp[:20] if isinstance(self.__mtApp, bytes) else self.__mtApp
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtClientId[:20]
|
||||
if isinstance(self.__mtClientId, bytes)
|
||||
else self.__mtClientId
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtEventTime[:20]
|
||||
if isinstance(self.__mtEventTime, bytes)
|
||||
else self.__mtEventTime
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtPageId[:20]
|
||||
if isinstance(self.__mtPageId, bytes)
|
||||
else self.__mtPageId
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtPageType[:20]
|
||||
if isinstance(self.__mtPageType, bytes)
|
||||
else self.__mtPageType
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtPrevPage[:20]
|
||||
if isinstance(self.__mtPrevPage, bytes)
|
||||
else self.__mtPrevPage
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtRequestId[:20]
|
||||
if isinstance(self.__mtRequestId, bytes)
|
||||
else self.__mtRequestId
|
||||
),
|
||||
limitedRepr(
|
||||
self.__mtTopic[:20]
|
||||
if isinstance(self.__mtTopic, bytes)
|
||||
else self.__mtTopic
|
||||
),
|
||||
limitedRepr(
|
||||
self.__needDiv[:20]
|
||||
if isinstance(self.__needDiv, bytes)
|
||||
else self.__needDiv
|
||||
),
|
||||
limitedRepr(self.__pg[:20] if isinstance(self.__pg, bytes) else self.__pg),
|
||||
limitedRepr(
|
||||
self.__price[:20] if isinstance(self.__price, bytes) else self.__price
|
||||
),
|
||||
limitedRepr(
|
||||
self.__pricingParameters[:20]
|
||||
if isinstance(self.__pricingParameters, bytes)
|
||||
else self.__pricingParameters
|
||||
),
|
||||
limitedRepr(
|
||||
self.__productType[:20]
|
||||
if isinstance(self.__productType, bytes)
|
||||
else self.__productType
|
||||
),
|
||||
limitedRepr(
|
||||
self.__salableAdamId[:20]
|
||||
if isinstance(self.__salableAdamId, bytes)
|
||||
else self.__salableAdamId
|
||||
),
|
||||
limitedRepr(
|
||||
self.__hasAskedToFulfillPreorder[:20]
|
||||
if isinstance(self.__hasAskedToFulfillPreorder, bytes)
|
||||
else self.__hasAskedToFulfillPreorder
|
||||
),
|
||||
limitedRepr(
|
||||
self.__buyWithoutAuthorization[:20]
|
||||
if isinstance(self.__buyWithoutAuthorization, bytes)
|
||||
else self.__buyWithoutAuthorization
|
||||
),
|
||||
limitedRepr(
|
||||
self.__hasDoneAgeCheck[:20]
|
||||
if isinstance(self.__hasDoneAgeCheck, bytes)
|
||||
else self.__hasDoneAgeCheck
|
||||
),
|
||||
limitedRepr(
|
||||
self.__hasConfirmedPaymentSheet[:20]
|
||||
if isinstance(self.__hasConfirmedPaymentSheet, bytes)
|
||||
else self.__hasConfirmedPaymentSheet
|
||||
),
|
||||
limitedRepr(
|
||||
self.__asn[:20]
|
||||
if isinstance(self.__asn, bytes)
|
||||
else self.__asn
|
||||
),
|
||||
)
|
||||
5758
src_mac/ipatool-py/reqs/schemas/store_buyproduct_resp.py
Executable file
5758
src_mac/ipatool-py/reqs/schemas/store_buyproduct_resp.py
Executable file
File diff suppressed because it is too large
Load Diff
160
src_mac/ipatool-py/reqs/schemas/store_download_req.py
Executable file
160
src_mac/ipatool-py/reqs/schemas/store_download_req.py
Executable file
@@ -0,0 +1,160 @@
|
||||
from reprlib import repr as limitedRepr
|
||||
|
||||
|
||||
class StoreDownloadReq:
|
||||
|
||||
_types_map = {
|
||||
"creditDisplay": {"type": str, "subtype": None},
|
||||
"guid": {"type": str, "subtype": None},
|
||||
"salableAdamId": {"type": str, "subtype": None},
|
||||
"appExtVrsId": {"type": str, "subtype": None},
|
||||
}
|
||||
_formats_map = {}
|
||||
_validations_map = {
|
||||
"creditDisplay": {
|
||||
"required": True,
|
||||
},
|
||||
"guid": {
|
||||
"required": True,
|
||||
},
|
||||
"salableAdamId": {
|
||||
"required": True,
|
||||
},
|
||||
"appExtVrsId": {
|
||||
"required": False,
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
creditDisplay: str = None,
|
||||
guid: str = None,
|
||||
salableAdamId: str = None,
|
||||
appExtVrsId: str = None,
|
||||
):
|
||||
pass
|
||||
self.__creditDisplay = creditDisplay
|
||||
self.__guid = guid
|
||||
self.__salableAdamId = salableAdamId
|
||||
self.__appExtVrsId = appExtVrsId
|
||||
|
||||
def _get_creditDisplay(self):
|
||||
return self.__creditDisplay
|
||||
|
||||
def _set_creditDisplay(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("creditDisplay must be str")
|
||||
|
||||
self.__creditDisplay = value
|
||||
|
||||
creditDisplay = property(_get_creditDisplay, _set_creditDisplay)
|
||||
|
||||
def _get_guid(self):
|
||||
return self.__guid
|
||||
|
||||
def _set_guid(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("guid must be str")
|
||||
|
||||
self.__guid = value
|
||||
|
||||
guid = property(_get_guid, _set_guid)
|
||||
|
||||
def _get_salableAdamId(self):
|
||||
return self.__salableAdamId
|
||||
|
||||
def _set_salableAdamId(self, value):
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("salableAdamId must be str")
|
||||
|
||||
self.__salableAdamId = value
|
||||
|
||||
salableAdamId = property(_get_salableAdamId, _set_salableAdamId)
|
||||
|
||||
def _get_appExtVrsId(self):
|
||||
return self.__appExtVrsId
|
||||
|
||||
def _set_appExtVrsId(self, value):
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("appExtVrsId must be str")
|
||||
|
||||
self.__appExtVrsId = value
|
||||
|
||||
appExtVrsId = property(_get_appExtVrsId, _set_appExtVrsId)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d):
|
||||
v = {}
|
||||
if "creditDisplay" in d:
|
||||
v["creditDisplay"] = (
|
||||
str.from_dict(d["creditDisplay"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["creditDisplay"]
|
||||
)
|
||||
if "guid" in d:
|
||||
v["guid"] = (
|
||||
str.from_dict(d["guid"]) if hasattr(str, "from_dict") else d["guid"]
|
||||
)
|
||||
if "salableAdamId" in d:
|
||||
v["salableAdamId"] = (
|
||||
str.from_dict(d["salableAdamId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["salableAdamId"]
|
||||
)
|
||||
if "appExtVrsId" in d:
|
||||
v["appExtVrsId"] = (
|
||||
str.from_dict(d["appExtVrsId"])
|
||||
if hasattr(str, "from_dict")
|
||||
else d["appExtVrsId"]
|
||||
)
|
||||
return StoreDownloadReq(**v)
|
||||
|
||||
def as_dict(self):
|
||||
d = {}
|
||||
if self.__creditDisplay is not None:
|
||||
d["creditDisplay"] = (
|
||||
self.__creditDisplay.as_dict()
|
||||
if hasattr(self.__creditDisplay, "as_dict")
|
||||
else self.__creditDisplay
|
||||
)
|
||||
if self.__guid is not None:
|
||||
d["guid"] = (
|
||||
self.__guid.as_dict()
|
||||
if hasattr(self.__guid, "as_dict")
|
||||
else self.__guid
|
||||
)
|
||||
if self.__salableAdamId is not None:
|
||||
d["salableAdamId"] = (
|
||||
self.__salableAdamId.as_dict()
|
||||
if hasattr(self.__salableAdamId, "as_dict")
|
||||
else self.__salableAdamId
|
||||
)
|
||||
if self.__appExtVrsId is not None:
|
||||
d["appExtVrsId"] = (
|
||||
self.__appExtVrsId.as_dict()
|
||||
if hasattr(self.__appExtVrsId, "as_dict")
|
||||
else self.__appExtVrsId
|
||||
)
|
||||
return d
|
||||
|
||||
def __repr__(self):
|
||||
return "<Class StoreDownloadReq. creditDisplay: {}, guid: {}, salableAdamId: {}, appExtVrsId: {}>".format(
|
||||
limitedRepr(
|
||||
self.__creditDisplay[:20]
|
||||
if isinstance(self.__creditDisplay, bytes)
|
||||
else self.__creditDisplay
|
||||
),
|
||||
limitedRepr(
|
||||
self.__guid[:20] if isinstance(self.__guid, bytes) else self.__guid
|
||||
),
|
||||
limitedRepr(
|
||||
self.__salableAdamId[:20]
|
||||
if isinstance(self.__salableAdamId, bytes)
|
||||
else self.__salableAdamId
|
||||
),
|
||||
limitedRepr(
|
||||
self.__appExtVrsId[:20]
|
||||
if isinstance(self.__appExtVrsId, bytes)
|
||||
else self.__appExtVrsId
|
||||
),
|
||||
)
|
||||
3910
src_mac/ipatool-py/reqs/schemas/store_download_resp.py
Executable file
3910
src_mac/ipatool-py/reqs/schemas/store_download_resp.py
Executable file
File diff suppressed because it is too large
Load Diff
254
src_mac/ipatool-py/reqs/store.py
Executable file
254
src_mac/ipatool-py/reqs/store.py
Executable file
@@ -0,0 +1,254 @@
|
||||
import hashlib
|
||||
import json
|
||||
import pickle
|
||||
import plistlib
|
||||
import requests
|
||||
from reqs.schemas.store_authenticate_req import StoreAuthenticateReq
|
||||
from reqs.schemas.store_authenticate_resp import StoreAuthenticateResp
|
||||
from reqs.schemas.store_buyproduct_req import StoreBuyproductReq
|
||||
from reqs.schemas.store_buyproduct_resp import StoreBuyproductResp
|
||||
from reqs.schemas.store_download_req import StoreDownloadReq
|
||||
from reqs.schemas.store_download_resp import StoreDownloadResp
|
||||
|
||||
class StoreException(Exception):
|
||||
def __init__(self, req, resp, errMsg, errType=None):
|
||||
self.req = req
|
||||
self.resp = resp # type: StoreDownloadResp
|
||||
self.errMsg = errMsg
|
||||
self.errType = errType
|
||||
super().__init__(
|
||||
"Store %s error: %s" % (self.req, self.errMsg) if not self.errType else
|
||||
"Store %s error: %s, errorType: %s" % (self.req, self.errMsg, self.errType)
|
||||
)
|
||||
|
||||
#CONFIGURATOR_UA = "Configurator/2.0 (Macintosh; OS X 10.12.6; 16G29) AppleWebKit/2603.3.8"
|
||||
CONFIGURATOR_UA = 'Configurator/2.0 (Macintosh; OS X 10.12.6; 16G29) AppleWebKit/2603.3.8 iOS/14.2 hwp/t8020'
|
||||
|
||||
class StoreClientAuth(object):
|
||||
def __init__(self, appleId=None, password=None):
|
||||
self.appleId = appleId
|
||||
self.password = password
|
||||
self.guid = None # the guid will not be used in itunes server mode
|
||||
self.accountName = None
|
||||
self.authHeaders = None
|
||||
self.authCookies = None
|
||||
|
||||
def __str__(self):
|
||||
return f"<{self.accountName} [{self.guid}]>"
|
||||
|
||||
def _generateGuid(self, appleId):
|
||||
'''
|
||||
Derive a GUID for an appleId. For each appleId, the GUID will always remain the same
|
||||
:param appleId:
|
||||
:return:
|
||||
'''
|
||||
DEFAULT_GUID = '000C2941396B' # this GUID is blocked
|
||||
# number of chars to use from DEFAULT_GUID as prefix (0..12)
|
||||
GUID_DEFAULT_PREFIX = 2
|
||||
# something unique
|
||||
GUID_SEED = 'CAFEBABE'
|
||||
# something between 0 and 30
|
||||
GUID_POS = 10
|
||||
|
||||
# generate a unique guid out of the appleId
|
||||
h = hashlib.sha1((GUID_SEED + appleId + GUID_SEED).encode("utf-8")).hexdigest()
|
||||
defaultPart = DEFAULT_GUID[:GUID_DEFAULT_PREFIX]
|
||||
hashPart = h[GUID_POS: GUID_POS + (len(DEFAULT_GUID) - GUID_DEFAULT_PREFIX)]
|
||||
guid = (defaultPart + hashPart).upper()
|
||||
return guid
|
||||
|
||||
def login(self, sess):
|
||||
if not self.guid:
|
||||
self.guid = self._generateGuid(self.appleId)
|
||||
|
||||
req = StoreAuthenticateReq(appleId=self.appleId, password=self.password, attempt='4', createSession="true",
|
||||
guid=self.guid, rmp='0', why='signIn')
|
||||
url = "https://p46-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate?guid=%s" % self.guid
|
||||
while True:
|
||||
r = sess.post(url,
|
||||
headers={
|
||||
"Accept": "*/*",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": CONFIGURATOR_UA,
|
||||
}, data=plistlib.dumps(req.as_dict()), allow_redirects=False)
|
||||
if r.status_code == 302:
|
||||
url = r.headers['Location']
|
||||
continue
|
||||
break
|
||||
d = plistlib.loads(r.content)
|
||||
resp = StoreAuthenticateResp.from_dict(d)
|
||||
if not resp.m_allowed:
|
||||
raise StoreException("authenticate", d, resp.customerMessage, resp.failureType)
|
||||
|
||||
self.authHeaders = {}
|
||||
self.authHeaders['X-Dsid'] = self.authHeaders['iCloud-Dsid'] = str(resp.download_queue_info.dsid)
|
||||
self.authHeaders['X-Apple-Store-Front'] = r.headers.get('x-set-apple-store-front')
|
||||
self.authHeaders['X-Token'] = resp.passwordToken
|
||||
self.authCookies = pickle.dumps(sess.cookies).hex()
|
||||
|
||||
self.accountName = resp.accountInfo.address.firstName + " " + resp.accountInfo.address.lastName
|
||||
def save(self):
|
||||
return json.dumps(self.__dict__)
|
||||
|
||||
@classmethod
|
||||
def load(cls, j):
|
||||
obj = json.loads(j)
|
||||
ret = cls()
|
||||
ret.__dict__.update(obj)
|
||||
return ret
|
||||
|
||||
class StoreClient(object):
|
||||
def __init__(self, sess: requests.Session):
|
||||
self.sess = sess
|
||||
self.iTunes_provider = None
|
||||
self.authInfo = None
|
||||
|
||||
def authenticate_load_session(self, sessionContent):
|
||||
self.authInfo = StoreClientAuth.load(sessionContent)
|
||||
if self.authInfo.authHeaders is None or self.authInfo.authCookies is None:
|
||||
raise Exception("invalid auth session")
|
||||
self.sess.headers = dict(self.authInfo.authHeaders)
|
||||
self.sess.cookies = pickle.loads(bytes.fromhex(self.authInfo.authCookies))
|
||||
|
||||
def authenticate_save_session(self):
|
||||
return self.authInfo.save()
|
||||
|
||||
def authenticate(self, appleId, password):
|
||||
if not self.authInfo:
|
||||
self.authInfo = StoreClientAuth(appleId, password)
|
||||
self.authInfo.login(self.sess)
|
||||
self.sess.headers = dict(self.authInfo.authHeaders)
|
||||
self.sess.cookies = pickle.loads(bytes.fromhex(self.authInfo.authCookies))
|
||||
|
||||
# ==> 🛠 [Verbose] Performing request: curl -k -X POST \
|
||||
# -H "iCloud-DSID: 12263680861" \
|
||||
# -H "Content-Type: application/x-www-form-urlencoded" \
|
||||
# -H "User-Agent: Configurator/2.0 (Macintosh; OS X 10.12.6; 16G29) AppleWebKit/2603.3.8" \
|
||||
# -H "X-Dsid: 12263680861" \
|
||||
# -d '<?xml version="1.0" encoding="UTF-8"?>
|
||||
# <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
# <plist version="1.0">
|
||||
# <dict>
|
||||
# <key>creditDisplay</key>
|
||||
# <string></string>
|
||||
# <key>guid</key>
|
||||
# <string>000C2941396B</string>
|
||||
# <key>salableAdamId</key>
|
||||
# <string>1239860606</string>
|
||||
# </dict>
|
||||
# </plist>
|
||||
# ' \
|
||||
# https://p25-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/volumeStoreDownloadProduct?guid=000C2941396Bk
|
||||
def volumeStoreDownloadProduct(self, appId, appVerId=""):
|
||||
req = StoreDownloadReq(creditDisplay="", guid=self.authInfo.guid, salableAdamId=appId, appExtVrsId=appVerId)
|
||||
hdrs = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": CONFIGURATOR_UA,
|
||||
}
|
||||
url = "https://p25-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/volumeStoreDownloadProduct?guid=%s" % self.authInfo.guid
|
||||
payload = req.as_dict()
|
||||
r = self.sess.post(url,
|
||||
headers=hdrs,
|
||||
data=plistlib.dumps(payload))
|
||||
d = plistlib.loads(r.content)
|
||||
resp = StoreDownloadResp.from_dict(d)
|
||||
if resp.cancel_purchase_batch:
|
||||
raise StoreException("volumeStoreDownloadProduct", d, resp.customerMessage, '%s-%s' % (resp.failureType, resp.metrics))
|
||||
return resp
|
||||
|
||||
def buyProduct(self, appId, appVer='', productType='C', pricingParameters='STDQ'):
|
||||
# STDQ - buy, STDRDL - redownload, SWUPD - update
|
||||
url = "https://p25-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/buyProduct"
|
||||
|
||||
itunes_internal = self.iTunes_provider(url)
|
||||
hdrs = itunes_internal.pop('headers')
|
||||
guid = itunes_internal.pop('guid')
|
||||
kbsync = itunes_internal.pop('kbsync')
|
||||
|
||||
if not appVer:
|
||||
from reqs.itunes import iTunesClient
|
||||
iTunes = iTunesClient(self.sess)
|
||||
appVer = iTunes.getAppVerId(appId, hdrs['X-Apple-Store-Front'])
|
||||
|
||||
req = StoreBuyproductReq(
|
||||
guid=guid,
|
||||
salableAdamId=str(appId),
|
||||
appExtVrsId=str(appVer) if appVer else None,
|
||||
|
||||
price='0',
|
||||
productType=productType,
|
||||
pricingParameters=pricingParameters,
|
||||
|
||||
ageCheck='true',
|
||||
hasBeenAuthedForBuy='true',
|
||||
isInApp='false',
|
||||
hasConfirmedPaymentSheet='true',
|
||||
asn='1',
|
||||
)
|
||||
payload = req.as_dict()
|
||||
payload['kbsync'] = kbsync # kbsync is bytes, but json schema does not support it, so we have to assign it
|
||||
if 'sbsync' in itunes_internal:
|
||||
payload['sbsync'] = itunes_internal.pop('sbsync') # sbsync is the same as kbsync
|
||||
if 'afds' in itunes_internal:
|
||||
payload['afds'] = itunes_internal.pop('afds')
|
||||
|
||||
hdrs = dict(hdrs)
|
||||
hdrs["Content-Type"] = "application/x-apple-plist"
|
||||
|
||||
r = self.sess.post(url,
|
||||
headers=hdrs,
|
||||
data=plistlib.dumps(payload)
|
||||
)
|
||||
|
||||
d = plistlib.loads(r.content)
|
||||
resp = StoreBuyproductResp.from_dict(d)
|
||||
if resp.cancel_purchase_batch:
|
||||
raise StoreException("buyProduct", d, resp.customerMessage, '%s-%s' % (resp.failureType, resp.metrics))
|
||||
return resp
|
||||
|
||||
def buyProduct_purchase(self, appId, productType='C'):
|
||||
url = "https://buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/buyProduct"
|
||||
req = StoreBuyproductReq(
|
||||
guid=self.authInfo.guid,
|
||||
salableAdamId=str(appId),
|
||||
appExtVrsId='0',
|
||||
|
||||
price='0',
|
||||
productType=productType,
|
||||
pricingParameters='STDQ',
|
||||
|
||||
hasAskedToFulfillPreorder='true',
|
||||
buyWithoutAuthorization='true',
|
||||
hasDoneAgeCheck='true',
|
||||
hasConfirmedPaymentSheet='true',
|
||||
)
|
||||
payload = req.as_dict()
|
||||
|
||||
r = self.sess.post(url,
|
||||
headers={
|
||||
"Content-Type": "application/x-apple-plist",
|
||||
"User-Agent": "Configurator/2.15 (Macintosh; OS X 11.0.0; 16G29) AppleWebKit/2603.3.8",
|
||||
},
|
||||
data=plistlib.dumps(payload))
|
||||
|
||||
if r.status_code == 500:
|
||||
raise StoreException("buyProduct_purchase", None, 'purchased_before')
|
||||
|
||||
d = plistlib.loads(r.content)
|
||||
resp = StoreBuyproductResp.from_dict(d)
|
||||
if resp.status != 0 or resp.jingleDocType != 'purchaseSuccess':
|
||||
raise StoreException("buyProduct_purchase", d, resp.customerMessage,
|
||||
'%s-%s' % (resp.status, resp.jingleDocType))
|
||||
return resp
|
||||
|
||||
def purchase(self, appId):
|
||||
if self.iTunes_provider:
|
||||
return None # iTunes mode will automatically purchase the app if not purchased
|
||||
else:
|
||||
return self.buyProduct_purchase(appId)
|
||||
|
||||
def download(self, appId, appVer='', isRedownload=True):
|
||||
if self.iTunes_provider:
|
||||
return self.buyProduct(appId, appVer, pricingParameters='STDRDL' if isRedownload else 'STDQ')
|
||||
else:
|
||||
return self.volumeStoreDownloadProduct(appId, appVer)
|
||||
2
src_mac/ipatool-py/requirements.txt
Executable file
2
src_mac/ipatool-py/requirements.txt
Executable file
@@ -0,0 +1,2 @@
|
||||
rich>=10.2.2
|
||||
requests>=2.25.0
|
||||
269
src_mac/lib.py
Executable file
269
src_mac/lib.py
Executable file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
from subprocess import run
|
||||
from typing import Dict, NamedTuple
|
||||
from zipfile import ZipFile
|
||||
import json
|
||||
import os
|
||||
import plistlib
|
||||
import shutil
|
||||
|
||||
from cfg import CONFIG, Log
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Types
|
||||
# ------------------------------
|
||||
|
||||
|
||||
VersionMap = Dict[int, str]
|
||||
|
||||
|
||||
class FlatVersion(NamedTuple):
|
||||
verId: int
|
||||
verOs: int
|
||||
|
||||
|
||||
class InfoPlist(NamedTuple):
|
||||
appId: int
|
||||
verId: int
|
||||
allVersions: 'list[int]'
|
||||
bundleId: str
|
||||
osVer: str
|
||||
|
||||
|
||||
class LocalIpaFile(NamedTuple):
|
||||
cracked: bool
|
||||
path: Path
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# IPA tool
|
||||
# ------------------------------
|
||||
|
||||
def ipaTool(*args: 'str|Path') -> None:
|
||||
# '--json'
|
||||
run(['python3', Path(__file__).parent/'ipatool-py'/'main.py'] + list(args))
|
||||
|
||||
|
||||
def ipaToolHistory(appId: int):
|
||||
Log.info('history for appid=%d', appId)
|
||||
ipaTool('historyver', '-s', CONFIG.itunes_server,
|
||||
'-o', CONFIG.download_tmp, '-i', str(appId))
|
||||
|
||||
|
||||
def ipaToolDownload(appId: int, verId: int):
|
||||
Log.info('download appid=%d verid=%d', appId, verId)
|
||||
ipaTool('download', '-s', CONFIG.itunes_server, '-o', CONFIG.download_tmp,
|
||||
'-i', str(appId), '--appVerId', str(verId))
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Path handling
|
||||
# ------------------------------
|
||||
|
||||
def pathForApp(appId: int) -> 'Path|None':
|
||||
return next(CONFIG.completed.glob(f'* - {appId}/'), None)
|
||||
|
||||
|
||||
def pathForIpa(appId: int, appVerId: int) -> 'Path|None':
|
||||
app_path = pathForApp(appId)
|
||||
if not app_path:
|
||||
return None
|
||||
return next(app_path.glob(f'* - {appVerId}.ipa'), None)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# IPA content reading
|
||||
# ------------------------------
|
||||
|
||||
def ipaReadInfoPlist(fname: Path) -> InfoPlist:
|
||||
with ZipFile(fname) as zip:
|
||||
itunesPlist = plistlib.loads(zip.read('iTunesMetadata.plist'))
|
||||
for entry in zip.filelist:
|
||||
p = entry.filename.split('/')
|
||||
if len(p) == 3 and p[0] == 'Payload' and p[2] == 'Info.plist':
|
||||
infoPlist = plistlib.loads(zip.read(entry))
|
||||
break
|
||||
return InfoPlist(
|
||||
itunesPlist['itemId'],
|
||||
itunesPlist['softwareVersionExternalIdentifier'],
|
||||
itunesPlist['softwareVersionExternalIdentifiers'],
|
||||
# itunesPlist['softwareVersionBundleId']
|
||||
infoPlist['CFBundleIdentifier'],
|
||||
infoPlist.get('MinimumOSVersion', '1.0'),
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Version Map
|
||||
# ------------------------------
|
||||
|
||||
def readVersionMap(appId: int) -> 'VersionMap|None':
|
||||
app_dir = pathForApp(appId)
|
||||
if app_dir:
|
||||
ver_map_json = app_dir / '_versions.json'
|
||||
if ver_map_json.exists():
|
||||
with open(ver_map_json, 'rb') as fp:
|
||||
data: dict[str, str] = json.load(fp)
|
||||
return {int(k): v for k, v in data.items()}
|
||||
return None
|
||||
|
||||
|
||||
def readVersionMapFromTemp(appId: int) -> 'VersionMap|None':
|
||||
hist_json = CONFIG.download_tmp / f'historyver_{appId}.json'
|
||||
if hist_json.exists():
|
||||
with open(hist_json, 'rb') as fp:
|
||||
allVerIds: list[int] = json.load(fp)['appVerIds']
|
||||
return {x: '' for x in allVerIds}
|
||||
return None
|
||||
|
||||
|
||||
def writeVersionMap(appId: int, data: VersionMap):
|
||||
app_dir = pathForApp(appId)
|
||||
assert app_dir, f'app dir must exist for {appId} before calling this.'
|
||||
|
||||
with open(app_dir / '_versions.json', 'w') as fp:
|
||||
json.dump(data, fp, indent=2, sort_keys=True)
|
||||
|
||||
hist_json = CONFIG.download_tmp / f'historyver_{appId}.json'
|
||||
if hist_json.exists():
|
||||
os.remove(hist_json)
|
||||
|
||||
|
||||
def updateVersionMap(fname: Path) -> InfoPlist:
|
||||
''' Returns iOS version string '''
|
||||
info = ipaReadInfoPlist(fname)
|
||||
if not pathForApp(info.appId):
|
||||
app_dir = CONFIG.completed / f'{info.bundleId} - {info.appId}'
|
||||
app_dir.mkdir(parents=True, exist_ok=True)
|
||||
data = readVersionMap(info.appId)
|
||||
if not data:
|
||||
data = readVersionMapFromTemp(info.appId)
|
||||
if not data:
|
||||
data: 'VersionMap|None' = {x: '' for x in info.allVersions}
|
||||
|
||||
assert data, f'by now, history json for {info.appId} should exist!'
|
||||
|
||||
if data.get(info.verId) != info.osVer:
|
||||
for x in info.allVersions:
|
||||
if x not in data:
|
||||
data[x] = ''
|
||||
data[info.verId] = info.osVer
|
||||
Log.info('update version for %s (%s)', info.appId, info.bundleId)
|
||||
writeVersionMap(info.appId, data)
|
||||
return info
|
||||
|
||||
|
||||
def flattenVersionMap(data: VersionMap) -> 'list[FlatVersion]':
|
||||
return sorted(FlatVersion(k, versionToInt(v)) for k, v in data.items())
|
||||
|
||||
|
||||
def loadFlatVersionMap(appId: int) -> 'list[FlatVersion]':
|
||||
data = readVersionMap(appId)
|
||||
if not data:
|
||||
data = readVersionMapFromTemp(appId)
|
||||
if not data: # needs download
|
||||
ipaToolHistory(appId)
|
||||
data = readVersionMapFromTemp(appId)
|
||||
if not data:
|
||||
raise RuntimeError(f'could not download version history for {appId}')
|
||||
return flattenVersionMap(data)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Helper
|
||||
# ------------------------------
|
||||
|
||||
def versionToInt(ver: str) -> int:
|
||||
if not ver:
|
||||
return 0
|
||||
major, minor, patch, *_ = ver.split('.') + [0, 0, 0]
|
||||
return int(major) * 1_00_00 + int(minor) * 1_00 + int(patch)
|
||||
|
||||
|
||||
def enumAppIds():
|
||||
return sorted([int(x.parent.name.split(' ')[-1])
|
||||
for x in CONFIG.completed.glob('*/_versions.json')])
|
||||
|
||||
|
||||
def downloadPath(appId: int, verId: int):
|
||||
return CONFIG.download_fix / f'{appId}.{verId}.ipa'
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Actual logic
|
||||
# ------------------------------
|
||||
|
||||
def downloadSpecificVersion(appId: int, verId: int) -> LocalIpaFile:
|
||||
ipa_path = pathForIpa(appId, verId)
|
||||
if ipa_path:
|
||||
return LocalIpaFile(True, ipa_path) # already cracked
|
||||
|
||||
download_path = downloadPath(appId, verId)
|
||||
if download_path.exists():
|
||||
return LocalIpaFile(False, download_path) # needs cracking
|
||||
|
||||
ipaToolDownload(appId, verId)
|
||||
tmp_file = next(CONFIG.download_tmp.glob(f'*-{appId}-{verId}.ipa'), None)
|
||||
if not tmp_file:
|
||||
raise RuntimeError(f'Could not download ipa {appId} {verId}')
|
||||
|
||||
shutil.move(tmp_file.as_posix(), download_path)
|
||||
updateVersionMap(download_path)
|
||||
return LocalIpaFile(False, download_path)
|
||||
|
||||
|
||||
def findLatestVersion(
|
||||
appId: int, maxOS: str, *, rmIncompatible: bool
|
||||
) -> 'int|None':
|
||||
ver_map = loadFlatVersionMap(appId)
|
||||
_maxOS = versionToInt(maxOS)
|
||||
|
||||
def proc_index(i: int) -> bool:
|
||||
verId, osVer = ver_map[i]
|
||||
if not osVer:
|
||||
ipa_file = downloadSpecificVersion(appId, verId)
|
||||
info = ipaReadInfoPlist(ipa_file.path)
|
||||
osVer = versionToInt(info.osVer)
|
||||
if rmIncompatible and osVer > _maxOS and not ipa_file.cracked:
|
||||
os.remove(ipa_file.path)
|
||||
Log.debug('app: %d ver: %d iOS: %s ...', appId, verId, osVer)
|
||||
return osVer <= _maxOS
|
||||
|
||||
if not proc_index(0):
|
||||
Log.warning(f'No compatible version for {appId}')
|
||||
return None
|
||||
|
||||
imin, imax = 1, len(ver_map) - 1
|
||||
best_i = 0
|
||||
while imin <= imax:
|
||||
i = imin + (imax - imin) // 2
|
||||
if proc_index(i):
|
||||
best_i = i
|
||||
imin = i + 1
|
||||
else:
|
||||
best_i = i - 1
|
||||
imax = i - 1
|
||||
return ver_map[best_i].verId
|
||||
|
||||
|
||||
def downloadAllUntil(
|
||||
idx: int, appId: int, maxOS: str, *, rmIncompatible: bool
|
||||
) -> 'Path|None':
|
||||
ver_map = loadFlatVersionMap(appId)
|
||||
_maxOS = versionToInt(maxOS)
|
||||
if idx >= len(ver_map):
|
||||
return None
|
||||
if any(x.verOs > _maxOS for x in ver_map[:idx + 1]):
|
||||
return None
|
||||
verId = ver_map[idx].verId
|
||||
ipa_file = downloadSpecificVersion(appId, verId)
|
||||
if ipa_file.cracked:
|
||||
return None
|
||||
info = ipaReadInfoPlist(ipa_file.path)
|
||||
osVer = versionToInt(info.osVer)
|
||||
if osVer <= _maxOS:
|
||||
return ipa_file.path
|
||||
elif rmIncompatible:
|
||||
os.remove(ipa_file.path)
|
||||
32
src_mac/move_em.py
Executable file
32
src_mac/move_em.py
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from cfg import CONFIG, Log
|
||||
from lib import ipaReadInfoPlist
|
||||
from server import WinServer
|
||||
|
||||
|
||||
def moveEmAll():
|
||||
for fname in CONFIG.sync_in.glob('*.ipa'):
|
||||
info = ipaReadInfoPlist(Path(fname))
|
||||
FROM = Path(fname)
|
||||
new_name = FROM.name[:-4] + f' - {info.verId}.ipa'
|
||||
DEST = next(CONFIG.completed.glob(f'{info.bundleId} */')) / new_name
|
||||
Log.info('[mv] -> %s', DEST.name)
|
||||
shutil.move(FROM.as_posix(), DEST)
|
||||
|
||||
# cleanup download files
|
||||
orig_filename = f'{info.appId}.{info.verId}.ipa'
|
||||
download_file = CONFIG.download_fix / orig_filename
|
||||
if download_file.exists():
|
||||
Log.info('[delete] %s', download_file)
|
||||
os.remove(download_file)
|
||||
|
||||
Log.info('[uninstall] %s', info.bundleId)
|
||||
WinServer.uninstall(info.bundleId)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
moveEmAll()
|
||||
69
src_mac/repack_ipa.py
Executable file
69
src_mac/repack_ipa.py
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from subprocess import run
|
||||
from zipfile import ZipFile
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from cfg import CONFIG, Log
|
||||
|
||||
_rx_coh = re.compile(r'\s*<key>storeCohort<\/key>\s*<string>[^<]*<\/string>')
|
||||
|
||||
|
||||
def cleanupZipDir(path: Path):
|
||||
if (path / 'IPAToolInfo.plist').exists():
|
||||
os.remove(path / 'IPAToolInfo.plist')
|
||||
shutil.rmtree(path / 'META-INF', ignore_errors=True)
|
||||
|
||||
if CONFIG.convert_plist:
|
||||
ii = next((path / 'Payload').glob('*.app')) / 'Info.plist'
|
||||
run(f'/usr/libexec/PlistBuddy -x -c print {ii} > {ii}.tmp', shell=True)
|
||||
os.remove(ii)
|
||||
os.rename(f'{ii}.tmp', ii)
|
||||
|
||||
with open(path / 'iTunesMetadata.plist', 'r') as fp:
|
||||
data = fp.read()
|
||||
start, end = _rx_coh.search(data).span() # type: ignore assume exist
|
||||
with open(path / 'iTunesMetadata.plist', 'w') as fp:
|
||||
fp.write(data[:start])
|
||||
fp.write(data[end:])
|
||||
|
||||
|
||||
def repackIpa(ipa_path: Path):
|
||||
is_dir = ipa_path.is_dir() and (ipa_path / 'IPAToolInfo.plist').exists()
|
||||
tmp_unzip_dir = ipa_path if is_dir else Path('tmp_unzip')
|
||||
|
||||
target_path = CONFIG.sync_out / ipa_path.name
|
||||
if is_dir: # in case of manual extraction (needed for utf8 filenames)
|
||||
just_app_ver_id = target_path.name.split(' ', 1)[0] # " 2" or " copy"
|
||||
target_path = target_path.with_name(just_app_ver_id + '.ipa')
|
||||
if target_path.exists():
|
||||
return
|
||||
|
||||
if ipa_path.is_file():
|
||||
Log.info('[unzip] %s', ipa_path)
|
||||
shutil.rmtree(tmp_unzip_dir, ignore_errors=True)
|
||||
tmp_unzip_dir.mkdir(exist_ok=True)
|
||||
|
||||
with ZipFile(ipa_path) as zip:
|
||||
zip.extractall(tmp_unzip_dir)
|
||||
else:
|
||||
Log.info('[repack-dir] %s', ipa_path)
|
||||
|
||||
cleanupZipDir(tmp_unzip_dir)
|
||||
|
||||
Log.info('[zip] %s', target_path)
|
||||
shutil.make_archive(str(target_path), 'zip', str(tmp_unzip_dir))
|
||||
shutil.move(str(target_path) + '.zip', str(target_path))
|
||||
shutil.rmtree(tmp_unzip_dir, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli = ArgumentParser()
|
||||
cli.add_argument('ipa', type=Path, nargs='+')
|
||||
args = cli.parse_args()
|
||||
|
||||
for fname in args.ipa:
|
||||
repackIpa(fname)
|
||||
35
src_mac/server.py
Executable file
35
src_mac/server.py
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
from urllib.request import urlopen
|
||||
|
||||
from cfg import CONFIG, Log
|
||||
|
||||
|
||||
class WinApiServer():
|
||||
def __init__(self) -> None:
|
||||
self.server_url = CONFIG.win_server
|
||||
if self._post('up', '') != 'YES':
|
||||
raise RuntimeError(
|
||||
f'WinServer {self.server_url} does not seem to be running')
|
||||
|
||||
def _post(self, action: str, data: str) -> str:
|
||||
''' With 10min timeout '''
|
||||
url = f'{self.server_url}/{action}'
|
||||
Log.debug('POST %s --data %s', url, data)
|
||||
with urlopen(url, data=data.encode('utf8'), timeout=1200) as fp:
|
||||
return fp.read().decode('utf8')
|
||||
|
||||
def install(self, fname: Path) -> str:
|
||||
if fname.suffix != '.ipa':
|
||||
raise ValueError(f'Not an *.ipa file: "{fname}"')
|
||||
if not fname.exists() or fname.is_dir():
|
||||
raise ValueError(f'File not found: "{fname}"')
|
||||
if fname.absolute().parent != CONFIG.sync_out.absolute():
|
||||
raise ValueError(f'Install file not in SYNC OUT dir: "{fname}"')
|
||||
return self._post('install', fname.name)
|
||||
|
||||
def uninstall(self, bundleId: str) -> str:
|
||||
return self._post('uninstall', bundleId)
|
||||
|
||||
|
||||
WinServer = WinApiServer()
|
||||
Reference in New Issue
Block a user