-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.ps1
More file actions
285 lines (248 loc) · 11.2 KB
/
Copy pathinstall.ps1
File metadata and controls
285 lines (248 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
<#
.SYNOPSIS
Amber 主题安装器 —— 定位 foobar2000 profile 目录,备份原配置,安装 Amber。
.DESCRIPTION
安装前会把 profile 下已有的 theme.fth 和 amber\ 目录完整备份到
profile\amber-backup-<时间戳>\,并写入 manifest.json 供 uninstall.ps1 精确还原。
.PARAMETER ProfileDir
显式指定 foobar2000 的 profile 目录。省略时自动探测。
.PARAMETER Layout
要安装的布局名(不含 .fth)。默认 amber-lite。
优先查找 themes\<Layout>.fth,回退到包根目录的 <Layout>.fth。
.PARAMETER ScriptsOnly
只更新 amber\ 里的面板脚本,不动 theme.fth。
给自己搭好布局的用户升级用 —— 覆盖 theme.fth 会把他们的布局冲掉。
.PARAMETER SkipConfirm
跳过"foobar2000 正在运行"以外的所有交互确认。
.EXAMPLE
.\install.ps1
.\install.ps1 -ProfileDir "D:\foobar2000\profile" -Layout amber-lite
.\install.ps1 -ScriptsOnly
.\install.ps1 -SkipConfirm
#>
[CmdletBinding()]
param(
[Parameter(Position=0)]
[string]$ProfileDir = '',
[Parameter(Position=1)]
[string]$Layout = 'amber-lite',
[switch]$ScriptsOnly,
[switch]$SkipConfirm
)
$ErrorActionPreference = 'Stop'
# PowerShell 5.1 的控制台默认不是 UTF-8,显式设置以免中文输出乱码
try { [Console]::OutputEncoding = [Text.Encoding]::UTF8 } catch { }
# 面向普通用户:只给一行人话,不甩 PowerShell 堆栈
trap {
Write-Host ''
Write-Host " × $($_.Exception.Message)" -ForegroundColor Red
Write-Host ''
Write-Host ' 未做任何改动。若已产生备份目录,可安全删除。' -ForegroundColor DarkGray
Write-Host ' 排查步骤见 INSTALL.md 的「常见问题」。' -ForegroundColor DarkGray
Write-Host ''
exit 1
}
$SrcDir = $PSScriptRoot
if (-not $SrcDir) { $SrcDir = Split-Path -Parent $MyInvocation.MyCommand.Definition }
function Write-Step ($m) { Write-Host " -> $m" }
function Write-Ok ($m) { Write-Host " OK $m" -ForegroundColor Green }
function Write-Warn2 ($m) { Write-Host " !! $m" -ForegroundColor Yellow }
Write-Host ''
Write-Host '=========================================='
Write-Host ' Amber — foobar2000 琥珀主题 安装器'
Write-Host '=========================================='
Write-Host ''
# ------------------------------------------------------------
# 1. 校验安装包自身完整性
# ------------------------------------------------------------
$srcAmber = Join-Path $SrcDir 'amber'
if (-not (Test-Path -LiteralPath $srcAmber -PathType Container)) {
throw "安装包不完整:找不到 amber\ 目录。请确认已把 zip 完整解压后再运行(不要在压缩包内直接双击)。"
}
# 布局文件:themes\<Layout>.fth 优先,回退到根目录 <Layout>.fth
# -ScriptsOnly 不写 theme.fth,布局文件缺失也无妨
$srcTheme = $null
if (-not $ScriptsOnly) {
foreach ($cand in @(
(Join-Path $SrcDir (Join-Path 'themes' "$Layout.fth")),
(Join-Path $SrcDir "$Layout.fth")
)) {
if (Test-Path -LiteralPath $cand -PathType Leaf) { $srcTheme = $cand; break }
}
if (-not $srcTheme) {
$avail = @()
foreach ($d in @((Join-Path $SrcDir 'themes'), $SrcDir)) {
if (Test-Path -LiteralPath $d) {
$avail += (Get-ChildItem -LiteralPath $d -Filter '*.fth' -ErrorAction SilentlyContinue |
ForEach-Object { $_.BaseName })
}
}
$avail = $avail | Select-Object -Unique
throw ("找不到布局 '$Layout'。可用布局:" + $(if ($avail) { $avail -join ', ' } else { '(无)' }))
}
}
# BOM 自检:BOM 丢失是最高频的"装完中文乱码"成因,装之前就拦下来
$noBom = @()
foreach ($js in Get-ChildItem -LiteralPath $srcAmber -Filter '*.js' -Recurse) {
$head = [byte[]](Get-Content -LiteralPath $js.FullName -Encoding Byte -TotalCount 3 -ErrorAction SilentlyContinue)
if ($head.Length -lt 3 -or $head[0] -ne 0xEF -or $head[1] -ne 0xBB -or $head[2] -ne 0xBF) {
$noBom += $js.Name
}
}
if ($noBom.Count -gt 0) {
Write-Warn2 ("以下脚本缺少 UTF-8 BOM,安装后面板内中文会乱码:" + ($noBom -join ', '))
Write-Warn2 "这通常是解压工具或 git 改写造成的,请重新下载官方 Release zip。"
if (-not $SkipConfirm) {
$go = Read-Host '仍要继续安装?(y/N)'
if ($go -notmatch '^[Yy]') { throw '已取消。' }
}
}
# ------------------------------------------------------------
# 2. 定位 profile 目录
# ------------------------------------------------------------
function Test-Fb2kProfile {
param([string]$Path)
if ([string]::IsNullOrWhiteSpace($Path)) { return $false }
if (-not (Test-Path -LiteralPath $Path -PathType Container)) { return $false }
# profile 目录的特征物之一
foreach ($marker in @('configuration', 'user-components-x64', 'user-components',
'theme.fth', 'config.sqlite', 'playlists-v2')) {
if (Test-Path -LiteralPath (Join-Path $Path $marker)) { return $true }
}
return $false
}
function Get-ProfileCandidates {
$list = New-Object System.Collections.Generic.List[string]
# 标准(非便携)安装
if ($env:APPDATA) { [void]$list.Add((Join-Path $env:APPDATA 'foobar2000-v2')) }
# 便携安装:解压目录自身或上溯几级里的 profile\
$d = $SrcDir
for ($i = 0; $i -lt 4 -and $d; $i++) {
[void]$list.Add((Join-Path $d 'profile'))
$parent = [IO.Path]::GetDirectoryName($d)
if ($parent -eq $d) { break }
$d = $parent
}
# 注册表登记的安装位置
foreach ($key in @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\foobar2000',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\foobar2000',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\foobar2000'
)) {
try {
$loc = (Get-ItemProperty -LiteralPath $key -ErrorAction Stop).InstallLocation
if ($loc) { [void]$list.Add((Join-Path $loc 'profile')) }
} catch { }
}
return $list
}
if ($ProfileDir) {
# 显式指定就必须成立,不静默回退——否则容易装进错误的目录
if (-not (Test-Fb2kProfile -Path $ProfileDir)) {
throw "指定的 -ProfileDir 不像 foobar2000 profile 目录:$ProfileDir"
}
$target = (Resolve-Path -LiteralPath $ProfileDir).Path
} else {
$target = $null
foreach ($c in Get-ProfileCandidates) {
if (Test-Fb2kProfile -Path $c) { $target = (Resolve-Path -LiteralPath $c).Path; break }
}
if (-not $target) {
Write-Warn2 '未能自动定位 foobar2000 profile 目录。'
Write-Host ' 便携版 = 安装目录下的 profile\ ;标准版 = %APPDATA%\foobar2000-v2'
$manual = Read-Host ' 请粘贴 profile 目录完整路径(直接回车取消)'
if (-not $manual) { throw '已取消。' }
if (-not (Test-Fb2kProfile -Path $manual)) { throw "该目录不像 foobar2000 profile:$manual" }
$target = (Resolve-Path -LiteralPath $manual).Path
}
}
Write-Ok "profile 目录:$target"
# ------------------------------------------------------------
# 3. 前置检查
# ------------------------------------------------------------
# foobar2000 运行中会在退出时把内存里的布局回写,覆盖掉我们刚装的 theme.fth
if (Get-Process -Name 'foobar2000' -ErrorAction SilentlyContinue) {
throw 'foobar2000 正在运行。请完全退出(含托盘图标)后重新运行本安装器。'
}
# JSplitter 缺失只警告不阻断——用户可能装在非常规位置
$jsFound = $false
foreach ($root in @($target, [IO.Path]::GetDirectoryName($target))) {
if ($root -and (Test-Path -LiteralPath $root)) {
if (Get-ChildItem -LiteralPath $root -Recurse -Filter 'foo_uie_jsplitter.dll' `
-ErrorAction SilentlyContinue | Select-Object -First 1) {
$jsFound = $true; break
}
}
}
if ($jsFound) {
Write-Ok 'JSplitter 组件已就位'
} else {
Write-Warn2 '未检测到 JSplitter (foo_uie_jsplitter)。缺少它 Amber 面板无法运行。'
Write-Warn2 '请从官方渠道安装该组件后再启动 foobar2000。'
}
# ------------------------------------------------------------
# 4. 备份
# ------------------------------------------------------------
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$backupDir = Join-Path $target "amber-backup-$stamp"
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
$dstTheme = Join-Path $target 'theme.fth'
$dstAmber = Join-Path $target 'amber'
$hadTheme = Test-Path -LiteralPath $dstTheme -PathType Leaf
$hadAmber = Test-Path -LiteralPath $dstAmber -PathType Container
if ($hadTheme -and -not $ScriptsOnly) {
Copy-Item -LiteralPath $dstTheme -Destination $backupDir -Force
Write-Step '已备份原 theme.fth'
}
if ($hadAmber) {
Copy-Item -LiteralPath $dstAmber -Destination (Join-Path $backupDir 'amber') -Recurse -Force
Write-Step '已备份原 amber\ 目录'
}
# 记录安装前的真实状态,让卸载可以精确还原(而不是猜)
# 版本号唯一来源是包内 manifest.json,与 tools\check-release.ps1 的门禁 5 一致。
# 早先这里从 CHANGELOG 标题正则抓,改个标题格式就静默变 'unknown'。
$version = 'unknown'
$pkgManifest = Join-Path $SrcDir 'manifest.json'
if (Test-Path -LiteralPath $pkgManifest) {
try {
$v = ([IO.File]::ReadAllText($pkgManifest, [Text.Encoding]::UTF8) | ConvertFrom-Json).version
if ($v) { $version = $v }
} catch { }
}
$manifest = [ordered]@{
tool = 'amber-install'
version = $version
layout = $(if ($ScriptsOnly) { $null } else { $Layout })
scriptsOnly = [bool]$ScriptsOnly
installedAt = (Get-Date -Format 'o')
profileDir = $target
hadThemeFth = $hadTheme
hadAmberDir = $hadAmber
}
($manifest | ConvertTo-Json) |
Set-Content -LiteralPath (Join-Path $backupDir 'manifest.json') -Encoding UTF8
Write-Ok "备份完成:$backupDir"
# ------------------------------------------------------------
# 5. 安装
# ------------------------------------------------------------
# 先删干净再复制:避免旧版本遗留的脚本文件混在新版本里
if ($hadAmber) { Remove-Item -LiteralPath $dstAmber -Recurse -Force }
Copy-Item -LiteralPath $srcAmber -Destination $dstAmber -Recurse -Force
Write-Step "已安装 amber\ ($((Get-ChildItem -LiteralPath $dstAmber -Filter '*.js' -Recurse).Count) 个脚本)"
if ($ScriptsOnly) {
Write-Step '已跳过 theme.fth(-ScriptsOnly),你的布局保持原样'
} else {
Copy-Item -LiteralPath $srcTheme -Destination $dstTheme -Force
Write-Step "已安装布局 $Layout.fth"
}
Write-Host ''
Write-Ok $(if ($ScriptsOnly) { 'Amber 面板脚本已更新。' } else { 'Amber 安装完成。' })
Write-Host ''
Write-Host ' 接下来:'
Write-Host ' 1. 启动 foobar2000'
Write-Host ' 2. 若面板空白或报「包未找到」,说明缺少 JSplitter 组件,'
Write-Host ' 装好后重启 foobar2000 即可。'
Write-Host ''
Write-Host " 想还原成安装前的样子:双击 uninstall.bat"
Write-Host " (备份保存在 $backupDir)"
Write-Host ''