chore: Update menu.sh to fix product version selection logic

This commit is contained in:
Ing 2024-08-28 19:29:18 +08:00
parent d44ba6504d
commit 168b6d5138
18 changed files with 6357 additions and 6363 deletions

View File

@ -31,6 +31,18 @@ def validate_required_param(ctx, param, value):
raise click.MissingParameter(param_decls=[param.name])
return value
def __fullversion(ver):
out = ver
arr = ver.split('-')
if len(arr) > 0:
a = arr[0].split('.')[0] if len(arr[0].split('.')) > 0 else '0'
b = arr[0].split('.')[1] if len(arr[0].split('.')) > 1 else '0'
c = arr[0].split('.')[2] if len(arr[0].split('.')) > 2 else '0'
d = arr[1] if len(arr) > 1 else '00000'
e = arr[2] if len(arr) > 2 else '0'
out = '{}.{}.{}-{}-{}'.format(a,b,c,d,e)
return out
@cli.command()
@click.option("-d", "--data", type=str, callback=mutually_exclusive_options, is_eager=True, help="The data of QRCode.")
@ -119,57 +131,210 @@ def getmodels(platforms=None):
PS = []
models = []
if len(models) == 0:
try:
req = session.get("https://autoupdate.synology.com/os/v2", timeout=10, verify=False)
req.encoding = "utf-8"
data = json.loads(req.text)
try:
req = session.get("https://autoupdate.synology.com/os/v2", timeout=10, verify=False)
req.encoding = "utf-8"
data = json.loads(req.text)
for I in data["channel"]["item"]:
if not I["title"].startswith("DSM"):
for I in data["channel"]["item"]:
if not I["title"].startswith("DSM"):
continue
for J in I["model"]:
arch = J["mUnique"].split("_")[1]
name = J["mLink"].split("/")[-1].split("_")[1].replace("%2B", "+")
if len(PS) > 0 and arch.lower() not in PS:
continue
for J in I["model"]:
arch = J["mUnique"].split("_")[1]
name = J["mLink"].split("/")[-1].split("_")[1].replace("%2B", "+")
if len(PS) > 0 and arch.lower() not in PS:
continue
if any(name == B["name"] for B in models):
continue
models.append({"name": name, "arch": arch})
models = sorted(models, key=lambda k: (k["arch"], k["name"]))
except:
pass
if len(models) == 0:
try:
import re
from bs4 import BeautifulSoup
# url="https://kb.synology.com/en-us/DSM/tutorial/What_kind_of_CPU_does_my_NAS_have"
url = "https://kb.synology.cn/zh-cn/DSM/tutorial/What_kind_of_CPU_does_my_NAS_have"
req = session.get(url, timeout=10, verify=False)
req.encoding = "utf-8"
bs = BeautifulSoup(req.text, "html.parser")
p = re.compile(r"data: (.*?),$", re.MULTILINE | re.DOTALL)
data = json.loads(p.search(bs.find("script", string=p).prettify()).group(1))
model = "(.*?)" # (.*?): all, FS6400: one
p = re.compile(r"<td>{}<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td>".format(model), re.MULTILINE | re.DOTALL,)
it = p.finditer(data["preload"]["content"].replace("\n", "").replace("\t", ""))
for i in it:
d = i.groups()
if len(d) == 6:
d = model + d
if len(PS) > 0 and d[5].lower() not in PS:
if any(name == B["name"] for B in models):
continue
models.append({"name": d[0].split("<br")[0], "arch": d[5].lower()})
except:
pass
models.append({"name": name, "arch": arch})
models = sorted(models, key=lambda k: (k["arch"], k["name"]))
except:
pass
models.sort(key=lambda x: (x["arch"], x["name"]))
print(json.dumps(models, indent=4))
@cli.command()
@click.option("-p", "--platforms", type=str, help="The platforms of Syno.")
def getmodelsbykb(platforms=None):
"""
Get Syno Models.
"""
import json, requests, urllib3
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry # type: ignore
adapter = HTTPAdapter(max_retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]))
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
if platforms is not None and platforms != "":
PS = platforms.lower().replace(",", " ").split()
else:
PS = []
models = []
try:
import re
from bs4 import BeautifulSoup
url="https://kb.synology.com/en-us/DSM/tutorial/What_kind_of_CPU_does_my_NAS_have"
#url = "https://kb.synology.cn/zh-cn/DSM/tutorial/What_kind_of_CPU_does_my_NAS_have"
req = session.get(url, timeout=10, verify=False)
req.encoding = "utf-8"
bs = BeautifulSoup(req.text, "html.parser")
p = re.compile(r"data: (.*?),$", re.MULTILINE | re.DOTALL)
data = json.loads(p.search(bs.find("script", string=p).prettify()).group(1))
model = "(.*?)" # (.*?): all, FS6400: one
p = re.compile(r"<td>{}<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td><td>(.*?)<\/td>".format(model), re.MULTILINE | re.DOTALL,)
it = p.finditer(data["preload"]["content"].replace("\n", "").replace("\t", ""))
for i in it:
d = i.groups()
if len(d) == 6:
d = model + d
if len(PS) > 0 and d[5].lower() not in PS:
continue
models.append({"name": d[0].split("<br")[0], "arch": d[5].lower()})
except:
pass
models.sort(key=lambda x: (x["arch"], x["name"]))
print(json.dumps(models, indent=4))
@cli.command()
@click.option("-m", "--model", type=str, required=True, help="The model of Syno.")
@click.option("-v", "--version", type=str, required=True, help="The version of Syno.")
def getpats4mv(model, version):
import json, requests, urllib3, re
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry # type: ignore
adapter = HTTPAdapter(max_retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]))
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
pats = {}
try:
urlInfo = "https://www.synology.com/api/support/findDownloadInfo?lang=en-us"
urlSteps = "https://www.synology.com/api/support/findUpgradeSteps?"
#urlInfo = "https://www.synology.cn/api/support/findDownloadInfo?lang=zh-cn"
#urlSteps = "https://www.synology.cn/api/support/findUpgradeSteps?"
major = "&major={}".format(version.split('.')[0]) if len(version.split('.')) > 0 else ""
minor = "&minor={}".format(version.split('.')[1]) if len(version.split('.')) > 1 else ""
req = session.get("{}&product={}{}{}".format(urlInfo, model.replace("+", "%2B"), major, minor), timeout=10, verify=False)
req.encoding = "utf-8"
data = json.loads(req.text)
build_ver = data['info']['system']['detail'][0]['items'][0]['build_ver']
build_num = data['info']['system']['detail'][0]['items'][0]['build_num']
buildnano = data['info']['system']['detail'][0]['items'][0]['nano']
V=__fullversion("{}-{}-{}".format(build_ver, build_num, buildnano))
if not V in pats:
pats[V]={}
pats[V]['url'] = data['info']['system']['detail'][0]['items'][0]['files'][0]['url'].split('?')[0]
pats[V]['sum'] = data['info']['system']['detail'][0]['items'][0]['files'][0]['checksum']
from_ver=0
for I in data['info']['pubVers']:
if from_ver == 0 or I['build'] < from_ver: from_ver = I['build']
for I in data['info']['productVers']:
if not I['version'].startswith(version): continue
if major == "" or minor == "":
majorTmp = "&major={}".format(I['version'].split('.')[0]) if len(I['version'].split('.')) > 0 else ""
minorTmp = "&minor={}".format(I['version'].split('.')[1]) if len(I['version'].split('.')) > 1 else ""
reqTmp = session.get("{}&product={}{}{}".format(urlInfo, model.replace("+", "%2B"), majorTmp, minorTmp), timeout=10, verify=False)
reqTmp.encoding = "utf-8"
dataTmp = json.loads(reqTmp.text)
build_ver = dataTmp['info']['system']['detail'][0]['items'][0]['build_ver']
build_num = dataTmp['info']['system']['detail'][0]['items'][0]['build_num']
buildnano = dataTmp['info']['system']['detail'][0]['items'][0]['nano']
V=__fullversion("{}-{}-{}".format(build_ver, build_num, buildnano))
if not V in pats:
pats[V]={}
pats[V]['url'] = dataTmp['info']['system']['detail'][0]['items'][0]['files'][0]['url'].split('?')[0]
pats[V]['sum'] = dataTmp['info']['system']['detail'][0]['items'][0]['files'][0]['checksum']
for J in I['versions']:
to_ver=J['build']
reqSteps = session.get("{}&product={}&from_ver={}&to_ver={}".format(urlSteps, model.replace("+", "%2B"), from_ver, to_ver), timeout=10, verify=False)
if reqSteps.status_code != 200: continue
reqSteps.encoding = "utf-8"
dataSteps = json.loads(reqSteps.text)
for S in dataSteps['upgrade_steps']:
if not 'full_patch' in S or S['full_patch'] is False: continue
if not 'build_ver' in S or not S['build_ver'].startswith(version): continue
V=__fullversion("{}-{}-{}".format(S['build_ver'], S['build_num'], S['nano']))
if not V in pats:
pats[V] = {}
pats[V]['url'] = S['files'][0]['url'].split('?')[0]
pats[V]['sum'] = S['files'][0]['checksum']
except:
pass
pats = {k: pats[k] for k in sorted(pats.keys(), reverse=True)}
print(json.dumps(pats, indent=4))
@cli.command()
@click.option("-p", "--models", type=str, help="The models of Syno.")
def getpats(models=None):
import json, requests, urllib3, re
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry # type: ignore
adapter = HTTPAdapter(max_retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]))
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
if models is not None and models != "":
MS = models.lower().replace(",", " ").split()
else:
MS = []
pats = {}
try:
req = session.get('https://archive.synology.com/download/Os/DSM', timeout=10, verify=False)
req.encoding = 'utf-8'
bs=BeautifulSoup(req.text, 'html.parser')
p = re.compile(r"(.*?)-(.*?)", re.MULTILINE | re.DOTALL)
l = bs.find_all('a', string=p)
for i in l:
ver = i.attrs['href'].split('/')[-1]
if not ver.startswith('7'): continue
req = session.get('https://archive.synology.com{}'.format(i.attrs['href']), timeout=10, verify=False)
req.encoding = 'utf-8'
bs=BeautifulSoup(req.text, 'html.parser')
p = re.compile(r"^(.*?)_(.*?)_(.*?).pat$", re.MULTILINE | re.DOTALL)
data = bs.find_all('a', string=p)
for item in data:
p = re.compile(r"DSM_(.*?)_(.*?).pat", re.MULTILINE | re.DOTALL)
rels = p.search(item.attrs['href'])
if rels != None:
info = p.search(item.attrs['href']).groups()
model = info[0].replace('%2B', '+')
if len(MS) > 0 and model.lower() not in MS:
continue
if model not in pats.keys():
pats[model]={}
pats[model][__fullversion(ver)] = item.attrs['href']
except:
pass
print(json.dumps(pats, indent=4))
if __name__ == "__main__":
cli()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -216,80 +216,57 @@ function productversMenu() {
[ $? -ne 0 ] && return 0
fi
else
arrayExistItem "${1}" ${ITEMS} || return 1
resp="${1}"
arrayExistItem "$(echo "${1}" | cut -d'.' -f1,2)" ${ITEMS} || return 1
resp="$(echo "${1}" | cut -d'.' -f1,2)"
fi
selver="${resp}"
urlver=""
paturl=""
patsum=""
# KVER=$(readConfigKey "platforms.${PLATFORM}.productvers.[${resp}].kver" "${WORK_PATH}/platforms.yml")
# if [ $(echo "${KVER:-4}" | cut -d'.' -f1) -lt 4 ] && [ -d "/sys/firmware/efi" ]; then
# if [ -z "${1}" ]; then
# DIALOG --title "$(TEXT "Product Version")" \
# --msgbox "$(TEXT "This version does not support UEFI startup, Please select another version or switch the startup mode.")" 0 0
# fi
# return 1
# fi
# if [ ! "usb" = "$(getBus "${LOADER_DISK}")" ] && [ $(echo "${KVER:-4}" | cut -d'.' -f1) -gt 4 ]; then
# if [ -z "${1}" ]; then
# DIALOG --title "$(TEXT "Product Version")" \
# --msgbox "$(TEXT "This version only support usb startup, Please select another version or switch the startup mode.")" 0 0
# fi
# return
# fi
if [ -z "${2}" -a -z "${3}" ]; then
while true; do
# get online pat data
idx=1
NETERR=0
while [ ${idx} -le 3 ]; do # Loop 3 times, if successful, break
if [ -z "${1}" ]; then
DIALOG --title "$(TEXT "Product Version")" \
--infobox "$(TEXT "Get pat data ...")" 0 0
fi
PJ="$(python ${WORK_PATH}/include/functions.py getpats4mv -m "${MODEL}" -v "${selver}")"
if [ -z "${PJ}" -o "${PJ}" = "{}" ]; then
if [ -z "${1}" ]; then
MSG="$(TEXT "Unable to connect to Synology website, Please check the network and try again, or use 'Parse Pat'!")"
DIALOG --title "$(TEXT "Addons")" \
--yes-label "$(TEXT "Retry")" \
--yesno "${MSG}" 0 0
[ $? -eq 0 ] && continue # yes-button
fi
return 1
else
PVS="$(echo "${PJ}" | jq -r 'keys | sort | reverse | join(" ")')"
if [ -z "${1}" ]; then
DIALOG --title "$(TEXT "Product Version")" \
--infobox "$(TEXT "Get pat data ...") (${idx}/3)" 0 0
fi
idx=$((${idx} + 1))
NETERR=0
fastest=$(_get_fastest "www.synology.com" "www.synology.cn")
if [ $? -ne 0 ]; then
NETERR=1
continue
fi
[ "${fastest}" = "www.synology.cn" ] &&
fastest="https://www.synology.cn/api/support/findDownloadInfo?lang=zh-cn" ||
fastest="https://www.synology.com/api/support/findDownloadInfo?lang=en-us"
patdata=$(curl -skL --connect-timeout 10 "${fastest}&product=${MODEL/+/%2B}&major=${selver%%.*}&minor=${selver##*.}")
if [ "$(echo ${patdata} | jq -r '.success' 2>/dev/null)" = "true" ]; then
if echo ${patdata} | jq -r '.info.system.detail[0].items[0].files[0].label_ext' 2>/dev/null | grep -q 'pat'; then
major=$(echo ${patdata} | jq -r '.info.system.detail[0].items[0].major')
minor=$(echo ${patdata} | jq -r '.info.system.detail[0].items[0].minor')
urlver="${major}.${minor}"
paturl=$(echo ${patdata} | jq -r '.info.system.detail[0].items[0].files[0].url')
patsum=$(echo ${patdata} | jq -r '.info.system.detail[0].items[0].files[0].checksum')
paturl=${paturl%%\?*}
break
fi
fi
done
if [ -z "${paturl}" -o -z "${patsum}" ]; then
if [ ${NETERR} -ne 0 ]; then
MSG=""
MSG+="$(TEXT "Unable to connect to Synology website, Please check the network and try again, or use 'Parse Pat'!")"
--no-items --menu "$(TEXT "Choose a pat version")" 0 0 0 ${PVS} \
2>${TMP_PATH}/resp
RET=$?
else
MSG="$(TEXT "Failed to get pat data,\nPlease manually fill in the URL and md5sum of the corresponding version of pat.\nOr click 'Retry'.")"
PV=""
[ -z "${PV}" ] && PV="$(echo "${PVS}" | tr ' ' '\n' | grep -xo "${1}" | head -n 1)"
[ -z "${PV}" ] && PV="$(echo "${PVS}" | cut -d' ' -f1)"
echo "${PV}" >"${TMP_PATH}/resp"
RET=0
fi
paturl=""
patsum=""
else
[ ${RET} -ne 0 ] && return
PV=$(cat ${TMP_PATH}/resp)
paturl=$(echo "${PJ}" | jq -r ".\"${PV}\".url")
patsum=$(echo "${PJ}" | jq -r ".\"${PV}\".sum")
urlver="$(echo "${PV}" | cut -d'.' -f1,2)"
fi
if [ -z "${1}" ]; then
MSG=""
MSG+="$(TEXT "Successfully to get pat data.")\n"
MSG+="$(TEXT "Please confirm or modify the URL and md5sum to you need.")"
if [ ! "${selver}" = "${urlver}" ]; then
MSG+="$(printf "$(TEXT "Note: There is no version %s and automatically returns to version %s.")" "${selver}" "${urlver}")\n"
selver=${urlver}
fi
MSG+="$(TEXT "Please confirm or modify the URL and md5sum to you need.")"
fi
if [ -z "${1}" ]; then
DIALOG --title "$(TEXT "Product Version")" \
--extra-button --extra-label "$(TEXT "Retry")" \
--form "${MSG}" 10 110 2 "URL" 1 1 "${paturl}" 1 5 100 0 "MD5" 2 1 "${patsum}" 2 5 100 0 \