You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

415 lines
14 KiB

# -*- coding: utf-8 -*-
# make_update_list.py
# 包的格式必须符合这个标准 arpg_cn_1.0.0.25.apk
import zipfile
import os.path
import os
import sys
import hashlib
import shutil
import platform
import time
# ************************************一些参数配置********************************************************star
class CFG:
# 是否比较全部包体,不是就比较gen_obb_file内容
IS_COMPARISION_ALL = True
# ************************************一些参数配置********************************************************end
Ipa_Streamingassets_Path = 'Payload/ChestQuestMiniRPG.app/Data/Raw'
Apk_Streamingassets_Path = 'assets'
Aab_Streamingassets_Path = "base_assets/assets"
Manifest_Suffix = '.manifest'
Bundle_Hash_File_Name = 'BundleHash.txt'
Bundle_Hash_Dict = {}
def CalcMD5(filepath):
with open(filepath, 'rb') as f:
md5obj = hashlib.md5()
md5obj.update(f.read())
hash = md5obj.hexdigest()
f.close()
return hash
def get_language_from_filename(filename):
return 'en'
# splitname=filename.split('_')
# return splitname[1]
def get_version_from_filename(filename):
splitname = filename.split('_')
# 兼容 arpg_google_oversea_1.0.162_20230207_234802.aab 类型文件名
# 当数组索引为2的item等于oversea时, 返回index为3的item
#return splitname[3] if splitname[2] == "oversea" else splitname[2]
return splitname[3] if "." in splitname[3] else splitname[2]
# end get_version_from_filename
#文件名字符串转时间戳
def get_daytime_from_filename(filename):
splitname = filename.split('_')
dayIndex = 3
if(splitname[2] == "oversea"):
dayIndex = 4
strday = splitname[dayIndex]
strday = strday[0:8]
strtime = "000000"
if(len(splitname) > dayIndex + 1):
strtimetmp = splitname[dayIndex + 1]
strtimetmp = strtimetmp[0:6]
#判断strtimetmp是数字还是字符串
if(strtimetmp.isdigit()):
strtime = strtimetmp
print('file:' + filename + ' day:' + strday + ' time:' + strtime)
return int(time.mktime(time.strptime(strday + ' ' + strtime, "%Y%m%d %H%M%S")))
# 获取版本main.mid.min.build中的build
def get_build_from_fullversion(strversion):
return int(strversion.split('.')[-1])
# end get_build_from_fullversion
# 获取版本main.mid.min.build中的main.mid.min
def get_version_from_fullversion(strversion):
sp = strversion.split('.')
return sp[0] + '.' + sp[1] + '.' + sp[2]
# end get_version_from_fullversion
# 补丁文件所在路径,不带版本号的
def get_patch_path_exclude_version(fileType, newapkname):
language = get_language_from_filename(newapkname)
strOs = 'android'
if fileType == 'ipa':
strOs = 'ios'
# [20220614] Linux环境生成路径修改
# rootPatchPath = f'/data/wwwroot/{fileType}_arpgpatch/'
rootPatchPath = f'./{fileType}_patch/'
if platform.system() == 'Windows':
rootPatchPath = f'./{fileType}_patch/'
return rootPatchPath + strOs + '/' + language
# end get_patch_path_exclude_version
# 补丁文件所在路径,带版本号的
def get_patch_path_include_version(fileType, newapkname):
newfullversion = get_version_from_filename(newapkname)
return get_patch_path_exclude_version(fileType, newapkname) + '/' + newfullversion
# end get_patch_path_include_version
def get_filelist_from_zip(zipfilename):
zf = zipfile.ZipFile(zipfilename, 'r')
zfinfolist = zf.infolist()
for zfinfo in zfinfolist:
filename = zfinfo.filename
print(filename)
zf.close()
return
# end get_filelist_from_zip
def make_patch_from_apk(fileType, path, newapkname, oldapkname, streampath):
# -4去掉.obb
newdir = newapkname[0:-4]
newlist = newdir + '.list'
olddir = oldapkname[0:-4]
oldlist = olddir + '.list'
oldlistpath = streampath + '/' + oldlist
language = get_language_from_filename(newapkname)
newfullversion = get_version_from_filename(newapkname)
oldfullversion = get_version_from_filename(oldapkname)
# 构造zip包的名字
diffzipname = 'pandora_' + language + '_' + oldfullversion + '_' + newfullversion + '.upk'
diffzippath = get_patch_path_include_version(fileType, newapkname)
print('make diff zip file: ' + diffzipname) # + ' diffzippath: '+diffzippath
print('make diff zip path: ' + diffzippath) # + ' diffzippath: '+diffzippath
if not os.path.exists(diffzippath):
os.makedirs(diffzippath)
zf = zipfile.ZipFile(diffzippath + '/' + diffzipname, 'w', zipfile.ZIP_DEFLATED)
olddict = {}
# print oldlistpath
# print 'oldlistpath: '+oldlistpath + ' streampath: '+streampath
file = open(oldlistpath, 'rt')
while True:
line = file.readline()
if line:
line = line.strip('\n')
# print line
olddict[line] = 1
# print 'oldline: '+line
else:
break
file.close()
file = open(streampath + '/' + newlist, 'rt')
for line in file:
line = line.strip('\n')
if not line in list(olddict.keys()):
keysp = line.split('\t')
# print 'keysp[0]:'+keysp[0]
filepathinzip = keysp[0]
if filepathinzip is not None:
filename = streampath + '/' + newdir + '/'
if fileType == 'obb' or fileType == 'apk':
filename = filename + Apk_Streamingassets_Path
elif fileType == 'aab':
filename = filename + Aab_Streamingassets_Path
else:
filename = filename + Ipa_Streamingassets_Path
filename = filename + '/' + keysp[0]
zf.write(filename, filepathinzip)
if os.path.exists(filename + Manifest_Suffix):
zf.write(filename + Manifest_Suffix, keysp[0] + Manifest_Suffix)
file.close()
zf.close()
return
# end make_diff_zip_from_apk
# 解压并且生成文件列表信息, 返回版本号
# path:apk保存目录 zipfilename:apk
def extract_and_make_filelist(path, zipfilename, fileType):
extractdirname = zipfilename[0:-4]
print('find file:' + zipfilename + ', try extract zip files to dir:' + extractdirname)
listfilename = extractdirname + '.list'
extractdirfull = path + '/' + extractdirname
# 从文件中读取版本号
fullversion = get_version_from_filename(zipfilename)
print('fullversion:' + fullversion)
if fullversion == None or fullversion == '':
return None
#之前是判断解压目录存在则不再重新解压,apk文件出错(比如zip格式损坏)的时候是有问题的,改成判断md5文件
#if not os.path.exists(extractdirfull) :
md5zipfilename = path + '/' + zipfilename + '.md5'
md5zipfileold = ''
if os.path.exists(md5zipfilename):
lf = open(md5zipfilename, 'r')
md5zipfileold = lf.read()
lf.close()
md5zipfilehash = CalcMD5(path + '/' + zipfilename)
if md5zipfilehash == md5zipfileold:
print('zipfile md5 is same ' + md5zipfilehash)
return fullversion
print('new zipfile md5 ' + md5zipfilehash + " old " + md5zipfileold)
print('extract zipfile to dir' + extractdirfull)
#如果存在解压目录,先删除
if os.path.exists(extractdirfull):
shutil.rmtree(extractdirfull)
zf = zipfile.ZipFile(path + '/' + zipfilename, 'r')
zf.extractall(extractdirfull)
zf.close()
dirneeddiff = extractdirfull
if fileType == 'obb' or fileType == 'apk':
dirneeddiff = extractdirfull + '/' + Apk_Streamingassets_Path
elif fileType == 'aab':
dirneeddiff = extractdirfull + '/' + Aab_Streamingassets_Path
else:
dirneeddiff = extractdirfull + '/' + Ipa_Streamingassets_Path
dirneeddiff = dirneeddiff.replace('\\', '/')
print(f"extract_and_make_filelist {zipfilename}")
iBuild = get_build_from_fullversion(fullversion)
# 需要忽略的目录
skipdiffdir = dirneeddiff + '/bin'
lf = open(path + '/' + listfilename, 'wt')
for root, dirs, files in os.walk(dirneeddiff):
for filename in files:
onefile = os.path.join(root, filename)
onefile = onefile.replace('\\', '/')
if onefile.find(skipdiffdir) != -1:
continue
namegroup = os.path.splitext(onefile)
# 忽略后缀是.manifest的文件
if namegroup[1] == Manifest_Suffix:
continue
# 计算MD5
filehash = CalcMD5(onefile)
# 把前面的绝对路径去掉
nPos = onefile.find(dirneeddiff)
onefile = onefile[nPos + len(dirneeddiff) + 1:]
fileinfostr = onefile + '\t' + filehash + '\n'
# print fileinfostr
lf.write(fileinfostr)
lf.close()
#全部成功后写入apk文件的md5文件,下次比较md5相等就可以提高效率
print('gen zipfile md5 ' + md5zipfilehash)
lf = open(md5zipfilename, 'wt')
lf.write(md5zipfilehash)
lf.close()
return fullversion
# end extract_and_make_filelist
# 获取配置比较的文件列表
def get_gen_apk_file_list(apkpath, fileType):
gen_list = []
# 不存在就读取全部
filelist = os.listdir(apkpath)
for filename in filelist:
if (filename.split('.')[-1] == fileType):
gen_list.append(filename)
#判断有没有相同版本的文件,如果有,则删除旧文件相关资源,只关心最后一个版本
iMaxBuild = 0
maxbuildapkfilename = ''
#重复的文件名
dupfilename = ''
for filename in gen_list:
fullversion = get_version_from_filename(filename)
if fullversion == None or fullversion == '':
return None
iBuild = get_build_from_fullversion(fullversion)
if (iBuild > iMaxBuild):
iMaxBuild = iBuild
maxbuildapkfilename = filename
elif (iBuild == iMaxBuild):
maxbuildtime = get_daytime_from_filename(maxbuildapkfilename)
filetime = get_daytime_from_filename(filename)
if(filetime > maxbuildtime):
dupfilename = maxbuildapkfilename
else:
dupfilename = filename
if(dupfilename != ''):
print('find dup version apk file ' + dupfilename + ', delete it')
gen_list.remove(dupfilename)
#删除解压目录
extractdirname = dupfilename[0:-4]
extractdirfull = apkpath + '/' + extractdirname
if(os.path.exists(extractdirfull)):
shutil.rmtree(extractdirfull)
#删除md5文件
md5zipfilename = apkpath + '/' + dupfilename + '.md5'
if(os.path.exists(md5zipfilename)):
os.remove(md5zipfilename)
#删除list文件
listfilename = extractdirfull + '.list'
if(os.path.exists(listfilename)):
os.remove(listfilename)
#删除zip文件
zipfilename = apkpath + '/' + dupfilename
if(os.path.exists(zipfilename)):
os.remove(zipfilename)
return gen_list
# fType 为apk或者ipa
def gen_all_patch_file_by_type(path, fileType):
print('begin gen_all_patch_file_by_type for all ' + fileType + ' files')
versionpath = os.path.abspath(path)
apkpath = versionpath + '/' + fileType
if not os.path.exists(apkpath):
print('no ' + fileType + ' dir exist in path ' + versionpath + ', skip.')
return
filelist = get_gen_apk_file_list(apkpath, fileType)
iMaxBuild = 0
newapkfilename = ''
newfullversion = ''
# 先获取最新的版本的那个文件
for filename in filelist:
fullversion = extract_and_make_filelist(apkpath, filename, fileType)
if fullversion == None or fullversion == '':
print('get version failed...' + filename)
return
iBuild = get_build_from_fullversion(fullversion)
if (iBuild > iMaxBuild):
iMaxBuild = iBuild
newapkfilename = filename
newfullversion = fullversion
print('last build file:' + newapkfilename)
if iMaxBuild == 0:
return
# 遍历所有obb和最新版本的obb进行差异比较
for filename in filelist:
if (filename != newapkfilename):
make_patch_from_apk(fileType, apkpath, newapkfilename, filename, apkpath)
# 最新包的路径
newapkPath = apkpath + '/' + newapkfilename
# 更新目录, /data/wwwroot/android/en
patchBasePath = get_patch_path_exclude_version(fileType, newapkfilename)
# copy完整的obb文件到版本更新目录
updatePatchPath = patchBasePath + '/' + newfullversion
if not os.path.exists(updatePatchPath):
os.makedirs(updatePatchPath)
shutil.copy(newapkPath, updatePatchPath)
# 最后写版本文件, 版本服务器会读这个文件
version_file = patchBasePath + '/version'
print('make version file for VersionServer: ' + version_file)
lf = open(version_file, 'wt')
lf.write(newfullversion)
lf.close()
print('Done.')
print('now version is:' + newfullversion)
# end gen_all_patch_file_by_type
if __name__ == '__main__':
platform_param = "all"
if len(sys.argv) == 2:
platform_param = sys.argv[1]
if platform_param == "all":
gen_all_patch_file_by_type('.', 'ipa')
gen_all_patch_file_by_type('.', 'aab')
gen_all_patch_file_by_type('.', 'apk')
elif platform_param == "ios":
gen_all_patch_file_by_type('.', 'ipa')
elif platform_param == "android":
gen_all_patch_file_by_type('.', 'obb')
elif platform_param == "aab":
gen_all_patch_file_by_type('.', 'aab')
elif platform_param == "apk":
gen_all_patch_file_by_type('.', 'apk')