|
|
|
@@ -109,8 +109,132 @@ def _offset_dir(version):
|
|
|
|
|
return path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _quote(path):
|
|
|
|
|
return '"' + str(path).replace('"', '""') + '"'
|
|
|
|
|
def _ps_quote(value):
|
|
|
|
|
return "'" + str(value).replace("'", "''") + "'"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def powershell_exe():
|
|
|
|
|
root = os.environ.get("SystemRoot") or os.environ.get("WINDIR") or r"C:\Windows"
|
|
|
|
|
path = os.path.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
|
|
|
|
|
return path if os.path.exists(path) else "powershell.exe"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_install_script(current, downloaded, pid, digest=""):
|
|
|
|
|
"""生成 Unicode 安全的更新脚本:校验副本后再交换文件,失败则从下载路径启动。"""
|
|
|
|
|
current = os.path.abspath(current)
|
|
|
|
|
downloaded = os.path.abspath(downloaded)
|
|
|
|
|
log_dir = _offset_dir("logs")
|
|
|
|
|
log_path = os.path.join(log_dir, "update.log")
|
|
|
|
|
script = r"""
|
|
|
|
|
$ErrorActionPreference = 'Continue'
|
|
|
|
|
$src = {src}
|
|
|
|
|
$dst = {dst}
|
|
|
|
|
$expected = {expected}
|
|
|
|
|
$logDir = {log_dir}
|
|
|
|
|
$log = {log}
|
|
|
|
|
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
|
|
|
|
|
function Write-UpdateLog($message) {{
|
|
|
|
|
try {{
|
|
|
|
|
Add-Content -LiteralPath $log -Encoding UTF8 -Value ("[" + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + "] " + $message)
|
|
|
|
|
}} catch {{}}
|
|
|
|
|
}}
|
|
|
|
|
Write-UpdateLog "update start; pid={pid}"
|
|
|
|
|
Start-Sleep -Seconds 2
|
|
|
|
|
try {{ Stop-Process -Id {pid} -Force -ErrorAction SilentlyContinue }} catch {{}}
|
|
|
|
|
Start-Sleep -Milliseconds 800
|
|
|
|
|
|
|
|
|
|
$dstDir = Split-Path -Parent $dst
|
|
|
|
|
$staged = $dst + ".update"
|
|
|
|
|
$backup = $dst + ".bak"
|
|
|
|
|
Remove-Item -LiteralPath $staged, $backup -Force -ErrorAction SilentlyContinue
|
|
|
|
|
$verified = $false
|
|
|
|
|
for ($i = 0; $i -lt 30; $i++) {{
|
|
|
|
|
try {{
|
|
|
|
|
Copy-Item -LiteralPath $src -Destination $staged -Force -ErrorAction Stop
|
|
|
|
|
if ($expected) {{
|
|
|
|
|
$actual = (Get-FileHash -LiteralPath $staged -Algorithm SHA256).Hash
|
|
|
|
|
if ($actual -ne $expected) {{ throw "SHA256 mismatch" }}
|
|
|
|
|
}}
|
|
|
|
|
$verified = $true
|
|
|
|
|
Write-UpdateLog "staged copy verified"
|
|
|
|
|
break
|
|
|
|
|
}} catch {{
|
|
|
|
|
Write-UpdateLog ("stage attempt " + $i + " failed: " + $_.Exception.Message)
|
|
|
|
|
Start-Sleep -Milliseconds 500
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
if (-not $verified) {{
|
|
|
|
|
Write-UpdateLog "stage failed; launching downloaded executable"
|
|
|
|
|
if (Test-Path -LiteralPath $src) {{
|
|
|
|
|
Start-Process -FilePath $src -WorkingDirectory (Split-Path -Parent $src)
|
|
|
|
|
}}
|
|
|
|
|
Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue
|
|
|
|
|
exit
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
$swapped = $false
|
|
|
|
|
for ($i = 0; $i -lt 30; $i++) {{
|
|
|
|
|
try {{
|
|
|
|
|
if (Test-Path -LiteralPath $dst) {{
|
|
|
|
|
Move-Item -LiteralPath $dst -Destination $backup -Force -ErrorAction Stop
|
|
|
|
|
}}
|
|
|
|
|
$swapped = $true
|
|
|
|
|
break
|
|
|
|
|
}} catch {{
|
|
|
|
|
Write-UpdateLog ("rename old attempt " + $i + " failed: " + $_.Exception.Message)
|
|
|
|
|
Start-Sleep -Milliseconds 500
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
if (-not $swapped) {{
|
|
|
|
|
Write-UpdateLog "cannot rename running exe; launching staged copy in place"
|
|
|
|
|
try {{
|
|
|
|
|
Move-Item -LiteralPath $staged -Destination $dst -Force -ErrorAction Stop
|
|
|
|
|
Start-Process -FilePath $dst -WorkingDirectory $dstDir
|
|
|
|
|
}} catch {{
|
|
|
|
|
Write-UpdateLog ("launch staged fallback failed: " + $_.Exception.Message)
|
|
|
|
|
if (Test-Path -LiteralPath $src) {{
|
|
|
|
|
Start-Process -FilePath $src -WorkingDirectory (Split-Path -Parent $src)
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue
|
|
|
|
|
exit
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
try {{
|
|
|
|
|
Move-Item -LiteralPath $staged -Destination $dst -Force -ErrorAction Stop
|
|
|
|
|
Start-Process -FilePath $dst -WorkingDirectory $dstDir -ErrorAction Stop
|
|
|
|
|
Write-UpdateLog "new version started"
|
|
|
|
|
for ($i = 0; $i -lt 15; $i++) {{
|
|
|
|
|
try {{
|
|
|
|
|
if (Test-Path -LiteralPath $backup) {{
|
|
|
|
|
[System.IO.File]::Delete($backup)
|
|
|
|
|
}}
|
|
|
|
|
Write-UpdateLog "old backup cleaned"
|
|
|
|
|
break
|
|
|
|
|
}} catch {{
|
|
|
|
|
Write-UpdateLog ("cleanup attempt " + $i + " failed: " + $_.Exception.Message)
|
|
|
|
|
Start-Sleep -Seconds 1
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
}} catch {{
|
|
|
|
|
Write-UpdateLog ("swap/start failed: " + $_.Exception.Message)
|
|
|
|
|
if ((Test-Path -LiteralPath $backup) -and -not (Test-Path -LiteralPath $dst)) {{
|
|
|
|
|
Move-Item -LiteralPath $backup -Destination $dst -Force -ErrorAction SilentlyContinue
|
|
|
|
|
}}
|
|
|
|
|
if (Test-Path -LiteralPath $src) {{
|
|
|
|
|
Start-Process -FilePath $src -WorkingDirectory (Split-Path -Parent $src)
|
|
|
|
|
}}
|
|
|
|
|
}}
|
|
|
|
|
Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue
|
|
|
|
|
""".format(
|
|
|
|
|
src=_ps_quote(downloaded),
|
|
|
|
|
dst=_ps_quote(current),
|
|
|
|
|
expected=_ps_quote(digest or ""),
|
|
|
|
|
log_dir=_ps_quote(log_dir),
|
|
|
|
|
log=_ps_quote(log_path),
|
|
|
|
|
pid=int(pid),
|
|
|
|
|
)
|
|
|
|
|
return script, log_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateDialog(QDialog):
|
|
|
|
@@ -367,24 +491,39 @@ class UpdateDialog(QDialog):
|
|
|
|
|
self.status.setText("安装包已打开")
|
|
|
|
|
return
|
|
|
|
|
current = os.path.abspath(sys.executable)
|
|
|
|
|
script_path = os.path.join(tempfile.gettempdir(), "netease_player_update.cmd")
|
|
|
|
|
script = "\r\n".join([
|
|
|
|
|
"@echo off",
|
|
|
|
|
"chcp 65001 >nul",
|
|
|
|
|
"timeout /t 2 /nobreak >nul",
|
|
|
|
|
"taskkill /PID %d /F >nul 2>&1" % os.getpid(),
|
|
|
|
|
'copy /Y %s %s >nul 2>&1' % (_quote(path), _quote(current)),
|
|
|
|
|
'start "" %s' % _quote(current),
|
|
|
|
|
'del "%~f0" >nul 2>&1',
|
|
|
|
|
"",
|
|
|
|
|
])
|
|
|
|
|
with open(script_path, "w", encoding="utf-8") as handle:
|
|
|
|
|
script, log_path = build_install_script(
|
|
|
|
|
current, path, os.getpid(), self.info.get("sha256") or "")
|
|
|
|
|
script_path = os.path.join(tempfile.gettempdir(), "netease_player_update.ps1")
|
|
|
|
|
with open(script_path, "w", encoding="utf-8-sig") as handle:
|
|
|
|
|
handle.write(script)
|
|
|
|
|
subprocess.Popen(
|
|
|
|
|
["cmd.exe", "/c", script_path],
|
|
|
|
|
creationflags=0x08000000 | 0x00000008,
|
|
|
|
|
close_fds=True,
|
|
|
|
|
)
|
|
|
|
|
try:
|
|
|
|
|
subprocess.Popen(
|
|
|
|
|
[
|
|
|
|
|
powershell_exe(), "-NoProfile", "-NonInteractive",
|
|
|
|
|
"-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden",
|
|
|
|
|
"-File", script_path,
|
|
|
|
|
],
|
|
|
|
|
creationflags=0x08000000 | 0x00000008,
|
|
|
|
|
close_fds=True,
|
|
|
|
|
)
|
|
|
|
|
except Exception as exc: # noqa: BLE001
|
|
|
|
|
# 更新脚本无法启动时,至少直接运行已下载的新版本
|
|
|
|
|
try:
|
|
|
|
|
subprocess.Popen(
|
|
|
|
|
[path],
|
|
|
|
|
cwd=os.path.dirname(path),
|
|
|
|
|
creationflags=0x00000008,
|
|
|
|
|
close_fds=True,
|
|
|
|
|
)
|
|
|
|
|
except Exception:
|
|
|
|
|
self.status.setText("无法启动更新程序:%s" % exc)
|
|
|
|
|
self.primary.setText("重试更新")
|
|
|
|
|
self.primary.setEnabled(True)
|
|
|
|
|
self.primary.setProperty("action", "install")
|
|
|
|
|
return
|
|
|
|
|
QApplication.quit()
|
|
|
|
|
self.status.setText("正在重启并安装,新版本会自动打开…")
|
|
|
|
|
self._notify("installing", "正在安装更新")
|
|
|
|
|
QApplication.quit()
|
|
|
|
|
|
|
|
|
|
def _notify(self, state, text):
|
|
|
|
|