feat: EH Downloader 用 Go + Wails v2 重写
原 Electron 版在这台 Windows 机器上无法启动(0x80000003 崩在 V8 初始化), 整个业务逻辑用 Go 重写,前端界面与协议结论完全保留。 - internal/eh: 协议核心,画廊页分页 -> /s/ -> hath 直链 -> 下载 - internal/core: 并发引擎 / 状态存储 / 目录模板 / 油猴桥口 - app.go: Wails 绑定层,替代 Electron IPC - frontend/js/api.js: 适配层,renderer.js 一行未改 - 37 个测试,含 httptest 模拟 EH 的端到端链路验证 相比 Electron 版:90MB -> 12.4MB,180MB 内存 -> 27MB,构建 90s -> 2s。 顺带修掉原版代理探测的 bug(永远返回第一个端口,从不等连接结果)。
This commit is contained in:
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
import {main} from '../models';
|
||||
import {core} from '../models';
|
||||
|
||||
export function AddTasks(arg1:string):Promise<main.AddTasksResult>;
|
||||
|
||||
export function ClearAll(arg1:boolean):Promise<Record<string, any>>;
|
||||
|
||||
export function ClearFinished():Promise<Record<string, any>>;
|
||||
|
||||
export function CountDone():Promise<number>;
|
||||
|
||||
export function ExportFailed(arg1:string):Promise<string>;
|
||||
|
||||
export function GetSettings():Promise<core.Settings>;
|
||||
|
||||
export function ListTasks():Promise<Array<core.Task>>;
|
||||
|
||||
export function OpenPath(arg1:string):Promise<void>;
|
||||
|
||||
export function PickDir():Promise<string>;
|
||||
|
||||
export function Placeholders():Promise<Array<string>>;
|
||||
|
||||
export function PreviewDir(arg1:string):Promise<main.DirPreviewResult>;
|
||||
|
||||
export function RemoveTask(arg1:string,arg2:string):Promise<Array<core.Task>>;
|
||||
|
||||
export function RequeueFailed():Promise<Array<core.Task>>;
|
||||
|
||||
export function SelectAll(arg1:Array<string>):Promise<Array<core.Task>>;
|
||||
|
||||
export function SetSettings(arg1:Record<string, any>):Promise<core.Settings>;
|
||||
|
||||
export function ShowItem(arg1:string):Promise<void>;
|
||||
|
||||
export function Start(arg1:Array<string>):Promise<main.StartResult>;
|
||||
|
||||
export function Status():Promise<main.StatusResult>;
|
||||
|
||||
export function Stop():Promise<Record<string, any>>;
|
||||
@@ -0,0 +1,79 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function AddTasks(arg1) {
|
||||
return window['go']['main']['App']['AddTasks'](arg1);
|
||||
}
|
||||
|
||||
export function ClearAll(arg1) {
|
||||
return window['go']['main']['App']['ClearAll'](arg1);
|
||||
}
|
||||
|
||||
export function ClearFinished() {
|
||||
return window['go']['main']['App']['ClearFinished']();
|
||||
}
|
||||
|
||||
export function CountDone() {
|
||||
return window['go']['main']['App']['CountDone']();
|
||||
}
|
||||
|
||||
export function ExportFailed(arg1) {
|
||||
return window['go']['main']['App']['ExportFailed'](arg1);
|
||||
}
|
||||
|
||||
export function GetSettings() {
|
||||
return window['go']['main']['App']['GetSettings']();
|
||||
}
|
||||
|
||||
export function ListTasks() {
|
||||
return window['go']['main']['App']['ListTasks']();
|
||||
}
|
||||
|
||||
export function OpenPath(arg1) {
|
||||
return window['go']['main']['App']['OpenPath'](arg1);
|
||||
}
|
||||
|
||||
export function PickDir() {
|
||||
return window['go']['main']['App']['PickDir']();
|
||||
}
|
||||
|
||||
export function Placeholders() {
|
||||
return window['go']['main']['App']['Placeholders']();
|
||||
}
|
||||
|
||||
export function PreviewDir(arg1) {
|
||||
return window['go']['main']['App']['PreviewDir'](arg1);
|
||||
}
|
||||
|
||||
export function RemoveTask(arg1, arg2) {
|
||||
return window['go']['main']['App']['RemoveTask'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function RequeueFailed() {
|
||||
return window['go']['main']['App']['RequeueFailed']();
|
||||
}
|
||||
|
||||
export function SelectAll(arg1) {
|
||||
return window['go']['main']['App']['SelectAll'](arg1);
|
||||
}
|
||||
|
||||
export function SetSettings(arg1) {
|
||||
return window['go']['main']['App']['SetSettings'](arg1);
|
||||
}
|
||||
|
||||
export function ShowItem(arg1) {
|
||||
return window['go']['main']['App']['ShowItem'](arg1);
|
||||
}
|
||||
|
||||
export function Start(arg1) {
|
||||
return window['go']['main']['App']['Start'](arg1);
|
||||
}
|
||||
|
||||
export function Status() {
|
||||
return window['go']['main']['App']['Status']();
|
||||
}
|
||||
|
||||
export function Stop() {
|
||||
return window['go']['main']['App']['Stop']();
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
export namespace core {
|
||||
|
||||
export class FailedItem {
|
||||
page: number;
|
||||
file: string;
|
||||
error: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new FailedItem(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.page = source["page"];
|
||||
this.file = source["file"];
|
||||
this.error = source["error"];
|
||||
}
|
||||
}
|
||||
export class Settings {
|
||||
downloadRoot: string;
|
||||
proxy: string;
|
||||
proxyMode: string;
|
||||
concurrency: number;
|
||||
timeout: number;
|
||||
xres: string;
|
||||
bridgePort: number;
|
||||
dirTemplate: string;
|
||||
_probed: boolean;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Settings(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.downloadRoot = source["downloadRoot"];
|
||||
this.proxy = source["proxy"];
|
||||
this.proxyMode = source["proxyMode"];
|
||||
this.concurrency = source["concurrency"];
|
||||
this.timeout = source["timeout"];
|
||||
this.xres = source["xres"];
|
||||
this.bridgePort = source["bridgePort"];
|
||||
this.dirTemplate = source["dirTemplate"];
|
||||
this._probed = source["_probed"];
|
||||
}
|
||||
}
|
||||
export class Task {
|
||||
gid: string;
|
||||
token: string;
|
||||
raw: string;
|
||||
title: string;
|
||||
status: string;
|
||||
done: number;
|
||||
failed: number;
|
||||
totalPages: number;
|
||||
error: string;
|
||||
dir: string;
|
||||
pages: Record<string, string>;
|
||||
failedList: FailedItem[];
|
||||
selected: boolean;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Task(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.gid = source["gid"];
|
||||
this.token = source["token"];
|
||||
this.raw = source["raw"];
|
||||
this.title = source["title"];
|
||||
this.status = source["status"];
|
||||
this.done = source["done"];
|
||||
this.failed = source["failed"];
|
||||
this.totalPages = source["totalPages"];
|
||||
this.error = source["error"];
|
||||
this.dir = source["dir"];
|
||||
this.pages = source["pages"];
|
||||
this.failedList = this.convertValues(source["failedList"], FailedItem);
|
||||
this.selected = source["selected"];
|
||||
this.createdAt = source["createdAt"];
|
||||
this.updatedAt = source["updatedAt"];
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice && a.map) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace main {
|
||||
|
||||
export class AddTasksResult {
|
||||
added: number;
|
||||
skipped: number;
|
||||
warning: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AddTasksResult(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.added = source["added"];
|
||||
this.skipped = source["skipped"];
|
||||
this.warning = source["warning"];
|
||||
}
|
||||
}
|
||||
export class DirPreviewResult {
|
||||
ok: boolean;
|
||||
path: string;
|
||||
error: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new DirPreviewResult(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.ok = source["ok"];
|
||||
this.path = source["path"];
|
||||
this.error = source["error"];
|
||||
}
|
||||
}
|
||||
export class StartResult {
|
||||
started: boolean;
|
||||
reason: string;
|
||||
count: number;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new StartResult(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.started = source["started"];
|
||||
this.reason = source["reason"];
|
||||
this.count = source["count"];
|
||||
}
|
||||
}
|
||||
export class StatusResult {
|
||||
running: boolean;
|
||||
queued: number;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new StatusResult(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.running = source["running"];
|
||||
this.queued = source["queued"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user