feat: 引入 so-novel submodule 并提供非交互下载工具
- vendor/so-novel: 上游 v1.12.0 (规则驱动的书源抓取/下载引擎) - tools/src: ToolMain 封装 search/download/toc/sources,stdout 只输出 JSON - tools/build.ps1: 自动定位/下载 JDK21,按平台显式激活 javet 原生库 profile - bin/novel.ps1|.cmd: 命令行入口(人读输出或 -Json) - SKILL.md/README.md: 使用说明与下载原理分析
This commit is contained in:
+127
@@ -0,0 +1,127 @@
|
||||
# Shared helpers for the ebook-skill command line (Windows PowerShell 5.1+ / pwsh 7+).
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Get-ProjectRoot {
|
||||
param([string]$Start = $PSScriptRoot)
|
||||
$dir = (Resolve-Path $Start).Path
|
||||
while ($dir) {
|
||||
if (Test-Path (Join-Path $dir 'vendor\so-novel\pom.xml')) { return $dir }
|
||||
$parent = Split-Path $dir -Parent
|
||||
if ($parent -eq $dir -or -not $parent) { break }
|
||||
$dir = $parent
|
||||
}
|
||||
throw "Cannot locate project root (vendor/so-novel/pom.xml not found above $Start). Run: git submodule update --init"
|
||||
}
|
||||
|
||||
function Get-ToolPaths {
|
||||
param([string]$Root = (Get-ProjectRoot))
|
||||
[pscustomobject]@{
|
||||
Root = $Root
|
||||
Vendor = Join-Path $Root 'vendor\so-novel'
|
||||
Tools = Join-Path $Root 'tools'
|
||||
Runtime = Join-Path $Root 'tools\runtime'
|
||||
Build = Join-Path $Root 'tools\build'
|
||||
Classes = Join-Path $Root 'tools\build\classes'
|
||||
AppJar = Join-Path $Root 'tools\runtime\app.jar'
|
||||
ToolJar = Join-Path $Root 'tools\runtime\sonovel-tool.jar'
|
||||
Config = Join-Path $Root 'tools\runtime\config.ini'
|
||||
Rules = Join-Path $Root 'tools\runtime\rules'
|
||||
Books = Join-Path $Root 'books'
|
||||
}
|
||||
}
|
||||
|
||||
function Test-Jdk21 {
|
||||
param([string]$JdkHome)
|
||||
if (-not $JdkHome) { return $false }
|
||||
$java = Join-Path $JdkHome 'bin\java.exe'
|
||||
if (-not (Test-Path $java)) { return $false }
|
||||
try {
|
||||
$out = (& $java -version 2>&1 | Out-String)
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
if ($out -match 'version "(?:1\.)?(\d+)') {
|
||||
return ([int]$Matches[1] -ge 21)
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
function Find-Jdk21 {
|
||||
param([switch]$AllowDownload)
|
||||
|
||||
$candidates = New-Object System.Collections.Generic.List[string]
|
||||
if ($env:EBOOK_NOVEL_JAVA_HOME) { $candidates.Add($env:EBOOK_NOVEL_JAVA_HOME) }
|
||||
if ($env:JAVA_HOME) { $candidates.Add($env:JAVA_HOME) }
|
||||
|
||||
$patterns = @(
|
||||
(Join-Path $PSScriptRoot '.jdk\*\*'),
|
||||
(Join-Path $PSScriptRoot '.jdk'),
|
||||
'C:\Program Files\Java\jdk-21*',
|
||||
'C:\Program Files\Eclipse Adoptium\jdk-21*',
|
||||
'C:\Program Files\JetBrains\*\jbr',
|
||||
(Join-Path $env:LOCALAPPDATA 'mise\installs\java\21*'),
|
||||
(Join-Path $env:LOCALAPPDATA 'mise\installs\java\2*')
|
||||
)
|
||||
foreach ($pattern in $patterns) {
|
||||
foreach ($dir in (Get-ChildItem -Path $pattern -Directory -ErrorAction SilentlyContinue)) {
|
||||
$candidates.Add($dir.FullName)
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($candidate in $candidates) {
|
||||
if (Test-Jdk21 -JdkHome $candidate) { return (Resolve-Path $candidate).Path }
|
||||
}
|
||||
|
||||
if ($AllowDownload) { return (Install-Jdk21) }
|
||||
return $null
|
||||
}
|
||||
|
||||
# Downloads a portable Temurin JDK 21 (used only when no local JDK 21 exists).
|
||||
function Install-Jdk21 {
|
||||
$dest = Join-Path $PSScriptRoot '.jdk'
|
||||
New-Item -ItemType Directory -Force $dest | Out-Null
|
||||
|
||||
Write-Host '[setup] no JDK 21 found, downloading Temurin 21 from the Tsinghua mirror ...' -ForegroundColor Yellow
|
||||
$mirror = 'https://mirrors.tuna.tsinghua.edu.cn/Adoptium/21/jdk/x64/windows/'
|
||||
$listing = (Invoke-WebRequest -Uri $mirror -UseBasicParsing -TimeoutSec 60).Content
|
||||
$names = [regex]::Matches($listing, 'OpenJDK21U-jdk_x64_windows_hotspot_[0-9._]+\.zip') |
|
||||
ForEach-Object { $_.Value } | Sort-Object -Unique
|
||||
if (-not $names) { throw "Could not find a JDK 21 archive at $mirror" }
|
||||
$zipName = $names[$names.Count - 1]
|
||||
|
||||
$zip = Join-Path $env:TEMP $zipName
|
||||
if (-not (Test-Path $zip)) {
|
||||
Invoke-WebRequest -Uri ($mirror + $zipName) -OutFile $zip -UseBasicParsing -TimeoutSec 1800
|
||||
}
|
||||
Expand-Archive -Path $zip -DestinationPath $dest -Force
|
||||
|
||||
$java = Get-ChildItem -Path (Join-Path $dest '*\bin\java.exe') -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if (-not $java) { throw "JDK extraction failed under $dest" }
|
||||
$JdkHome = Split-Path (Split-Path $java.FullName -Parent) -Parent
|
||||
Write-Host "[setup] JDK 21 installed at $JdkHome" -ForegroundColor Green
|
||||
return $JdkHome
|
||||
}
|
||||
|
||||
function Get-JavaCommand {
|
||||
param([string]$Root = (Get-ProjectRoot))
|
||||
$jdkHome = Find-Jdk21
|
||||
if (-not $jdkHome) {
|
||||
throw 'JDK 21 is required. Run tools\build.ps1 (it can download one), or set EBOOK_NOVEL_JAVA_HOME.'
|
||||
}
|
||||
return (Join-Path $jdkHome 'bin\java.exe')
|
||||
}
|
||||
|
||||
function Find-JarTool {
|
||||
# JBR ships without jar.exe, so fall back to any other JDK on the machine.
|
||||
foreach ($pattern in @('C:\Program Files\Java\*\bin\jar.exe',
|
||||
(Join-Path $env:LOCALAPPDATA 'mise\installs\java\*\bin\jar.exe'),
|
||||
'C:\Program Files\Eclipse Adoptium\*\bin\jar.exe')) {
|
||||
$exe = Get-ChildItem -Path $pattern -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($exe) { return $exe.FullName }
|
||||
}
|
||||
$cmd = Get-Command jar.exe -ErrorAction SilentlyContinue
|
||||
if ($cmd) { return $cmd.Source }
|
||||
return $null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user