ReVancedBuilder/ReVancedBuilder.py

191 lines
No EOL
6.3 KiB
Python
Executable file

#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu>
# SPDX-License-Identifier: GPL-3.0-only
import sys
import os
import configparser as cp
import requests as req
import json
from packaging.version import Version
from APKPure_dl import *
from JAVABuilder import *
from datetime import datetime
from Notifications import send_notif
from Cleanup import *
import logging
import subprocess
# TODO: README
# TODO: PATCHES_GUIDE.md (maybe delete it?)
# TODO: Install using pip
# Update the ReVanced tools, if needed
def update_tools(appstate):
for item in ['revanced-cli', 'revanced-integrations', 'revanced-patches']:
*_, tool = filter(lambda x: x['repository'] == 'revanced/'+item, tools) # Get the last result
latest_ver = Version(tool['version'])
try:
present_ver = Version(appstate['present_vers'][item])
except KeyError:
present_ver = Version('0')
output_file = item+os.path.splitext(tool['name'])[1]
if flag == 'force' or not os.path.isfile(output_file) or present_ver < latest_ver:
appstate['up-to-date'] = False
print(f"{item} has an update ({str(present_ver)} -> {str(latest_ver)})")
if flag != 'checkonly':
print(f"Downloading {output_file}...")
res = req.get(tool['browser_download_url'], stream=True)
res.raise_for_status()
with open(output_file, 'wb') as f:
for chunk in res.iter_content(chunk_size=8192):
f.write(chunk)
appstate['present_vers'].update({item: str(latest_ver)})
print("Done!")
return appstate
# Update microG, if needed
def update_microg(appstate):
try:
data = req.get('https://api.github.com/repos/inotia00/VancedMicroG/releases/latest').json()['tag_name']
latest_ver = Version(data)
except req.exceptions.RequestException as e:
clean_exit(e, appstate)
try:
present_ver = Version(appstate['present_vers']['VancedMicroG'])
except KeyError:
present_ver = Version('0')
if flag == 'force' or not os.path.isfile('microg.apk') or present_ver < latest_ver:
appstate['up-to-date'] = False
print(f"Vanced microG has an update ({str(present_ver)} -> {str(latest_ver)})")
if flag != 'checkonly':
print(f"Downloading vanced-microg.apk...")
res = req.get('https://github.com/inotia00/VancedMicroG/releases/latest/download/microg.apk', stream=True)
res.raise_for_status()
with open('microg.apk', 'wb') as f:
for chunk in res.iter_content(chunk_size=8192):
f.write(chunk)
appstate['present_vers'].update({'VancedMicroG': str(latest_ver)})
print("Done!")
appstate['microg_updated'] = True
return appstate
# ------------------------------
# The main function starts here
# ------------------------------
# Create a dict for storing important data
appstate = {}
# Get a timestamp
time = datetime.now()
appstate['timestamp'] = time.strftime('%Y%m%d%H%M%S')
# Read arguments
try:
os.chdir(sys.argv[1])
except IndexError:
clean_exit('Please provide a working directory as argument!', appstate)
except FileNotFoundError:
clean_exit('Invalid working directory provided!', appstate)
# Try to make sure only one instance is running in a given working directory
try:
if os.path.exists('lockfile'):
raise FileExistsError
with open('tmplockfile', 'x') as f:
f.flush()
os.fsync(f.fileno())
os.replace('tmplockfile', 'lockfile')
except FileExistsError:
sys.exit('Another instance is already running in the same working directory!')
# Set up logging
try:
os.mkdir('logs')
except FileExistsError:
pass
logging.basicConfig(level=logging.INFO, format='%(message)s')
logger = logging.getLogger()
logger.addHandler(logging.FileHandler(f"logs/{appstate['timestamp']}.log", 'w'))
print = logger.info
appstate['logger'] = logger
# Get the flag
try:
flag = sys.argv[2]
except:
flag = None
if flag not in ['buildonly', 'checkonly', 'force', 'experimental', None]:
clean_exit(f"Unknown flag: {flag}", appstate)
appstate['flag'] = flag
appstate['microg_updated'] = False
print(f"Started building ReVanced apps at {time.strftime('%d %B, %Y %H:%M:%S')}")
print('----------------------------------------------------------------------')
# Read configs
try:
appstate['build_config']=cp.ConfigParser()
appstate['build_config'].read_file(open('build_config.toml', 'r'))
except FileNotFoundError:
clean_exit('No build config provided, exiting. Please look at the GitHub page for more information:\n https://github.com/SinTan1729/ReVancedBuilder', appstate)
appstate['notification_config'] = cp.ConfigParser()
appstate['notification_config'].read('notification_config.toml')
# Pull the latest information using the ReVanced API
try:
tools = req.get('https://releases.revanced.app/tools').json()['tools']
except req.exceptions.RequestException as e:
clean_exit(e, appstate)
try:
with open('versions.json', 'r') as f:
appstate['present_vers'] = json.load(f)
except:
# We'll treat empty as 0 later
appstate['present_vers'] = json.loads('{}')
appstate['up-to-date'] = True
# send_notif(appstate, error=False) # <,,,,,,,,<,,,,,,,,,,,,,
if flag != 'buildonly':
appstate = update_tools(appstate)
appstate = update_microg(appstate)
if not appstate['up-to-date'] or flag == 'force':
appstate = get_apks(appstate)
if (flag != 'checkonly' and not appstate['up-to-date']) or flag in ['force', 'buildonly']:
build_apps(appstate)
move_apps(appstate)
# Update version numbers in the versions.json file
if appstate['up-to-date'] and flag != 'buildonly':
print('There\'s nothing to do.')
elif flag != ['checkonly']:
send_notif(appstate)
try:
os.rename('versions.json', 'versions-old.json')
except FileNotFoundError:
pass
if flag != 'buildonly':
with open('versions.json', 'w') as f:
json.dump(appstate['present_vers'], f, indent=4)
try:
subprocess.run(f"{appstate['build_config']['post_script']['file']} {timestamp}", shell=True)
except:
pass
# Delete the lockfile
os.remove('lockfile')