#!/usr/bin/env python3 """Xiaomi Firmware Updater autoamtion script - By XiaomiFirmwareUpdater""" # pylint: disable=too-many-locals,too-many-branches,too-many-statements import subprocess from datetime import datetime, date from glob import glob from hashlib import md5 from os import remove, system, environ, path, getcwd, chdir, rename from urllib.error import HTTPError import yaml from github3 import GitHub, exceptions from helpers import set_region, set_version, md5_check, set_folder from hurry.filesize import size, alternative from post_updates import post_updates from axel import axel from requests import get GIT_OAUTH_TOKEN = environ['XFU'] GIT = GitHub(token=GIT_OAUTH_TOKEN) WORK_DIR = getcwd() ARB_DEVICES = ['nitrogen', 'nitrogen_global', 'sakura', 'sakura_india_global', 'wayne'] STABLE = {} WEEKLY = {} VARIANTS = ['stable'] def initialize(): """ Initial loading and preparing """ remove("create_flashable_firmware.py") axel("https://raw.githubusercontent.com/XiaomiFirmwareUpdater/xiaomi-flashable-firmware-creator.py/py/xiaomi_flashable_firmware_creator/create_flashable_firmware.py") with open('devices/stable_devices.yml', 'r') as stable_json: stable_devices = yaml.load(stable_json, Loader=yaml.CLoader) open('log', 'w').close() all_stable = yaml.load(get( "https://raw.githubusercontent.com/XiaomiFirmwareUpdater/miui-updates-tracker/master/" + "stable_recovery/stable_recovery.yml").text, Loader=yaml.CLoader) names = yaml.load(get( "https://raw.githubusercontent.com/XiaomiFirmwareUpdater/miui-updates-tracker/master/" + "devices/names.yml").text, Loader=yaml.CLoader) return stable_devices, all_stable, names def diff(name: str) -> list: """ compare yaml files """ changes = [] try: with open(f'{name}.yml', 'r') as new, \ open(f'{name}_old.yml', 'r') as old_data: latest = yaml.load(new, Loader=yaml.CLoader) old = yaml.load(old_data, Loader=yaml.CLoader) first_run = False except FileNotFoundError: print(f"Can't find old {name} files, skipping") first_run = True if first_run is False: if len(latest) == len(old): codenames = list(latest.keys()) for codename in codenames: if not latest[codename] == old[codename]: changes.append({codename: latest[codename]}) else: new_codenames = [i for i in list(latest.keys()) if i not in list(old.keys())] for codename in new_codenames: changes.append({codename: latest[codename]}) return changes def upload_fw(file, version, codename, today, variant): """ Upload files to GitHub release """ print("uploading: " + file) codename = codename.split('-')[0] folder = set_folder(file) subprocess.call(['rclone', 'copy', file, 'osdn:/storage/groups/x/xi/xiaomifirmwareupdater/' + folder + '/' + version + '/' + codename + '/', '-v']) repository = GIT.repository('XiaomiFirmwareUpdater', f'firmware_xiaomi_{codename}') tag = f'{variant}-{today}' try: release = repository.release_from_tag(tag) # release exist already except exceptions.NotFoundError: # create new release release = repository.create_release(tag, name=tag, body= f"Extracted Firmware from MIUI {file.split('_')[4]}", draft=False, prerelease=False) try: asset = release.upload_asset(content_type='application/binary', name=file, asset=open(file, 'rb')) print(f'Uploaded {asset.name} Successfully to release {release.name}') except exceptions.UnprocessableEntity: print(f'{file} is already uploaded') def upload_non_arb(file, version, codename): """ Uploads non-arb firmware to OSDN """ print("uploading: " + file) codename = codename.split('-')[0] folder = set_folder(file) subprocess.call(['rclone', 'copy', file, 'osdn:/storage/groups/x/xi/xiaomifirmwareupdater/non-arb/' + folder + '/' + version + '/' + codename + '/', '-v']) def log_new(file, branch): """ Writes new changes to log file """ with open('log', 'a') as log: codename = str(file).split("_")[1] model = str(file).split("_")[3] version = str(file).split("_")[4].strip() android = str(file).split("_")[6].split(".zip")[0] try: zip_size = size(path.getsize(file), system=alternative) md5_hash = md5_check(file) except NameError: pass if str(file).split("_")[0] == 'fw': var = 'firmware' elif str(file).split("_")[0] == 'fw-non-arb': var = 'non-arb firmware' try: log.write(var + '|' + branch + '|' + model + '|' + codename + '|' + version + '|' + android + '|' + file + '|' + zip_size + '|' + md5_hash + '\n') except NameError: pass def git_commit_push(): """ git add - git commit - git push """ now = str(datetime.today()).split('.')[0] system("git add stable.yml && "" \ ""git -c \"user.name=XiaomiFirmwareUpdater\" " "-c \"user.email=xiaomifirmwareupdater@gmail.com\" " "commit -m \"sync: {0}\" && "" \ ""git push -q https://{1}@github.com/XiaomiFirmwareUpdater/" "mi-firmware-updater.git HEAD:master" .format(now, GIT_OAUTH_TOKEN)) def update_site(): """ update the website files """ chdir("../xiaomifirmwareupdater.github.io/data_generator/") subprocess.call(['python3', 'generator.py']) now = str(datetime.today()).split('.')[0] system("git add ../data ../pages ../releases.xml && "" \ ""git -c \"user.name=XiaomiFirmwareUpdater\" " "-c \"user.email=xiaomifirmwareupdater@gmail.com\" " "commit -m \"sync: {}\" && "" \ ""git push -q https://{}@github.com/XiaomiFirmwareUpdater/" "xiaomifirmwareupdater.github.io.git master" .format(now, GIT_OAUTH_TOKEN)) chdir(WORK_DIR) def main(): """ XiaomiFirmwareUpdater """ branch = '' devices = '' devices_all = None stable_devices, all_stable, names = initialize() for variant in VARIANTS: if path.exists(variant + '.yml'): rename(variant + '.yml', variant + '_old.yml') if variant == 'stable': devices_all = all_stable devices = stable_devices branch = STABLE # elif variant == "weekly": # devices_all = all_weekly # devices = weekly_devices # branch = WEEKLY for i in devices_all: codename = str(i["codename"]) if codename in devices: try: branch.update({codename: str(i["filename"]).split('_')[1] + '_' + str(i["filename"]).split('_')[2]}) except IndexError: continue with open(variant + '.yml', 'w') as output: yaml.dump(branch, output, Dumper=yaml.CDumper) # diff changes = diff(variant) print(variant + " changes:\n" + str(changes)) with open(variant + '_changes', 'w') as output: output.write(str(changes)) if not changes: continue to_update = [list(i.keys())[0] for i in changes] # get links links = {} for codename in to_update: try: links.update({codename: [i["download"] for i in devices_all if i["codename"] == codename][0]}) except IndexError: continue # download and generate fw for codename, url in links.items(): file = url.split('/')[-1] version = file.split("_")[2] # check if rom is rolled-back old_data = yaml.load(get( "https://raw.githubusercontent.com/XiaomiFirmwareUpdater/" + "xiaomifirmwareupdater.github.io/master/data/devices/" + f"full/{codename.split('_')[0]}.yml").text, Loader=yaml.CLoader) if old_data == {404: 'Not Found'}: print(f"Working on {codename} for the first time!") old_data = [] region = set_region(file) if 'V' in version: all_versions = [i for i in old_data if i['branch'] == 'stable'] else: all_versions = [i for i in old_data if i['branch'] == 'weekly'] check = [i for i in all_versions if i['versions']['miui'] == version and i['region'] == region] if check: print(f"{codename}: {version} is rolled back ROM, skipping!") continue # start working print("Starting download " + file) axel(url.replace("bigota", "airtel.bigota"), WORK_DIR, num_connections=128) if codename in ARB_DEVICES: subprocess.call(['python3', 'create_flashable_firmware.py', '-F', file]) subprocess.call(['python3', 'create_flashable_firmware.py', '-N', file]) else: subprocess.call(['python3', 'create_flashable_firmware.py', '-F', file]) remove(file) # upload to OSDN/GitHub today = str(date.today().strftime('%d.%m.%Y')) for file in glob("fw_*.zip"): codename = str(file).split("_")[1] version = set_version(file) upload_fw(file, version, codename, today, variant) for file in glob("fw-non-arb_*.zip"): codename = str(file).split("_")[1] version = set_version(file) upload_non_arb(file, version, codename) # log the made files stable = [f for f in glob("fw*.zip") if "_V1" in f] for file in stable: branch = 'stable' log_new(file, branch) weekly = [f for f in glob("fw*.zip") if "_V1" not in f] for file in weekly: branch = 'weekly' log_new(file, branch) for file in glob("*.zip"): remove(file) git_commit_push() update_site() post_updates(names) if __name__ == '__main__': main()