mirror of
https://github.com/lifegpc/eh-downloader.git
synced 2026-06-26 05:56:47 +08:00
Update
This commit is contained in:
5
db.ts
5
db.ts
@@ -524,6 +524,11 @@ export class EhDb {
|
||||
);
|
||||
return s.length ? s[0] : undefined;
|
||||
}
|
||||
get_tasks() {
|
||||
return this.transaction(() =>
|
||||
this.db.queryEntries<Task>("SELECT * FROM task;")
|
||||
);
|
||||
}
|
||||
get_tasks_by_pid(pid: number) {
|
||||
return this.transaction(() =>
|
||||
this.db.queryEntries<Task>("SELECT * FROM task WHERE pid = ?;", [
|
||||
|
||||
2
deno.lock
generated
2
deno.lock
generated
@@ -197,6 +197,7 @@
|
||||
"https://esm.sh/*@preact/[email protected]": "f1591d7185a00b6f96fdf5f72a99bb7dde37c0e946c8854da71db6b99d430947",
|
||||
"https://esm.sh/*[email protected]": "88ec8d8706b6a3f1e0fdad3862a2690dcd9b350d87bdc8e7bd0e27fbc0f7d29e",
|
||||
"https://esm.sh/[email protected]/": "16d82ee0a75451f75b42d9a20db4da0ccae7ccc8cc09a41c73b4488aba010b94",
|
||||
"https://esm.sh/gh/SortableJS/[email protected]/Sortable.min.js": "0a3e3cf471bf4566d7a3f9823b1d0d3b2bd075404e93419cd4074bd23310a9b0",
|
||||
"https://esm.sh/[email protected]/Button": "bc60923d511c6e2e33a7064339b3e643a9c15e3ef232ab063ef570af2ef83dc8",
|
||||
"https://esm.sh/[email protected]/Checkbox": "bf34f5cd8c6d015916d854d91aab2caf115463e97be9a461f8dd3370ea11a49c",
|
||||
"https://esm.sh/[email protected]/Dialog": "b0ff8da9c770456748f7e065fecda2fc90f5364ea66cae75ff5f51d57f6a87eb",
|
||||
@@ -319,6 +320,7 @@
|
||||
"https://esm.sh/v124/[email protected]/deno/twind.mjs": "ac4bf729653ee66349a518ee4949f22e84b7f626e8f74de1066e798a7c1ca12a",
|
||||
"https://esm.sh/v124/[email protected]/sheets/sheets.d.ts": "9cd4663d180023e49d9379777c8926347f3f79bf3bee546e7e5be1524fe55e70",
|
||||
"https://esm.sh/v124/[email protected]/twind.d.ts": "48c49da7d770f1236ec8a9397af053e6fb5a2bedacf431f2189eeecc1468c01b",
|
||||
"https://esm.sh/v125/gh/SortableJS/[email protected]/denonext/Sortable.min.js": "aeba191bb6622c4ad41ae5d8f5d4dd6bf15074f264cb3640ed223fbaf052263b",
|
||||
"https://raw.githubusercontent.com/lucacasonato/esbuild_deno_loader/8031f71afa1bbcd3237a94b11f53a2e5c5c0e7bf/deps.ts": "b7248e5b750be62613a9417f407e65ed43726d83b11f9631d6dbb58634bbd7d1",
|
||||
"https://raw.githubusercontent.com/lucacasonato/esbuild_deno_loader/8031f71afa1bbcd3237a94b11f53a2e5c5c0e7bf/mod.ts": "3e507379372361162f93325a216b86f6098defb5bb60144555b507bca26d061f",
|
||||
"https://raw.githubusercontent.com/lucacasonato/esbuild_deno_loader/8031f71afa1bbcd3237a94b11f53a2e5c5c0e7bf/src/deno.ts": "71bee6b14e72ca193c0686d8b4f1f47d639a64745b6f5c7576f7a3616f436f57",
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as $3 from "./routes/api/task.ts";
|
||||
import * as $4 from "./routes/index.tsx";
|
||||
import * as $$0 from "./islands/Container.tsx";
|
||||
import * as $$1 from "./islands/Settings.tsx";
|
||||
import * as $$2 from "./islands/TaskManager.tsx";
|
||||
|
||||
const manifest = {
|
||||
routes: {
|
||||
@@ -22,6 +23,7 @@ const manifest = {
|
||||
islands: {
|
||||
"./islands/Container.tsx": $$0,
|
||||
"./islands/Settings.tsx": $$1,
|
||||
"./islands/TaskManager.tsx": $$2,
|
||||
},
|
||||
baseUrl: import.meta.url,
|
||||
config,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"twind/": "https://esm.sh/[email protected]/",
|
||||
"$std/": "https://deno.land/[email protected]/",
|
||||
"preact-material-components/": "https://esm.sh/[email protected]/",
|
||||
"accept-language-parser/": "https://esm.sh/[email protected]/"
|
||||
"accept-language-parser/": "https://esm.sh/[email protected]/",
|
||||
"sortable": "https://esm.sh/gh/SortableJS/[email protected]/Sortable.min.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import StyleSheet from "../components/StyleSheet.tsx";
|
||||
import { GlobalCtx } from "../components/GlobalContext.tsx";
|
||||
import Settings from "./Settings.tsx";
|
||||
import t, { i18n_map, I18NMap } from "../server/i18n.ts";
|
||||
import TaskManager from "./TaskManager.tsx";
|
||||
|
||||
export type ContainerProps = {
|
||||
i18n: I18NMap;
|
||||
@@ -76,6 +77,14 @@ export default class Container extends Component<ContainerProps> {
|
||||
>
|
||||
<Icon>home</Icon>
|
||||
</List.Item>
|
||||
<List.Item
|
||||
onClick={() => {
|
||||
set_display(false);
|
||||
set_state("#/task_manager");
|
||||
}}
|
||||
>
|
||||
<Icon>task</Icon>
|
||||
</List.Item>
|
||||
<List.Item
|
||||
onClick={() => {
|
||||
set_display(false);
|
||||
@@ -87,6 +96,7 @@ export default class Container extends Component<ContainerProps> {
|
||||
</List>
|
||||
<div class="main">
|
||||
<Settings show={state === "#/settings"} />
|
||||
<TaskManager show={state === "#/task_manager"} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
79
islands/TaskManager.tsx
Normal file
79
islands/TaskManager.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Component, ContextType } from "preact";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
import { GlobalCtx } from "../components/GlobalContext.tsx";
|
||||
import { TaskDetail, TaskStatus } from "../task.ts";
|
||||
import { Sortable } from "sortable";
|
||||
import { TaskClientSocketData, TaskServerSocketData } from "../server/task.ts";
|
||||
import { get_ws_host } from "../server/utils.ts";
|
||||
|
||||
export type TaskManagerProps = {
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
export default class TaskManager extends Component<TaskManagerProps> {
|
||||
static contextType = GlobalCtx;
|
||||
declare context: ContextType<typeof GlobalCtx>;
|
||||
render() {
|
||||
if (!this.props.show) return null;
|
||||
const [tasks, set_tasks] = useState<Map<number, TaskDetail>>(new Map());
|
||||
const ul = useRef<HTMLDivElement>();
|
||||
useEffect(() => {
|
||||
new Sortable(ul.current, {
|
||||
onSort: (evt: CustomEvent) => {
|
||||
console.log(evt);
|
||||
},
|
||||
});
|
||||
const ws = new WebSocket(`${get_ws_host()}/api/task`);
|
||||
console.log(ws);
|
||||
function sendMessage(mes: TaskClientSocketData) {
|
||||
ws.send(JSON.stringify(mes));
|
||||
}
|
||||
ws.onopen = () => {
|
||||
sendMessage({ type: "task_list" });
|
||||
};
|
||||
ws.onmessage = (e) => {
|
||||
const t: TaskServerSocketData = JSON.parse(e.data);
|
||||
if (t.type == "close") {
|
||||
ws.close();
|
||||
} else if (t.type == "tasks") {
|
||||
set_tasks((tasks) => {
|
||||
t.tasks.forEach((ta) => {
|
||||
tasks.set(ta.id, {
|
||||
base: ta,
|
||||
status: t.running.includes(ta.id)
|
||||
? TaskStatus.Running
|
||||
: TaskStatus.Wait,
|
||||
});
|
||||
});
|
||||
this.forceUpdate();
|
||||
return tasks;
|
||||
});
|
||||
} else if (t.type == "new_task") {
|
||||
set_tasks((tasks) => {
|
||||
tasks.set(t.detail.id, {
|
||||
base: t.detail,
|
||||
status: TaskStatus.Wait,
|
||||
});
|
||||
this.forceUpdate();
|
||||
return tasks;
|
||||
});
|
||||
}
|
||||
};
|
||||
self.addEventListener("beforeunload", () => {
|
||||
sendMessage({ type: "close" });
|
||||
});
|
||||
}, []);
|
||||
console.log(tasks.size);
|
||||
return (
|
||||
<div class="task_manager">
|
||||
<div
|
||||
id="task-list"
|
||||
// @ts-ignore checked
|
||||
ref={ul}
|
||||
>
|
||||
{Array.from(tasks.keys()).map((k) => <div>{k}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { get_task_manager } from "../../server.ts";
|
||||
import { Task, TaskProgress } from "../../task.ts";
|
||||
import { DiscriminatedUnion } from "../../utils.ts";
|
||||
|
||||
type EventMap = {
|
||||
close: Record<PropertyKey, never>;
|
||||
new_download_task: { gid: number; token: string };
|
||||
new_export_zip_task: { gid: number; output?: string };
|
||||
};
|
||||
|
||||
type EventData = DiscriminatedUnion<"type", EventMap>;
|
||||
import {
|
||||
TaskClientSocketData,
|
||||
TaskServerSocketData,
|
||||
} from "../../server/task.ts";
|
||||
|
||||
export const handler: Handlers<Task[]> = {
|
||||
GET(req, _ctx) {
|
||||
@@ -24,6 +19,9 @@ export const handler: Handlers<Task[]> = {
|
||||
t.removeEventListener("task_finished", handle);
|
||||
t.removeEventListener("task_progress", handle);
|
||||
};
|
||||
function sendMessage(mes: TaskServerSocketData) {
|
||||
socket.send(JSON.stringify(mes));
|
||||
}
|
||||
socket.onclose = () => {
|
||||
removeListener();
|
||||
};
|
||||
@@ -33,13 +31,22 @@ export const handler: Handlers<Task[]> = {
|
||||
};
|
||||
socket.onmessage = (e) => {
|
||||
try {
|
||||
const d: EventData = JSON.parse(e.data);
|
||||
const d: TaskClientSocketData = JSON.parse(e.data);
|
||||
if (d.type == "close") {
|
||||
sendMessage({ type: "close" });
|
||||
socket.close();
|
||||
} else if (d.type == "new_download_task") {
|
||||
t.add_download_task(d.gid, d.token);
|
||||
} else if (d.type == "new_export_zip_task") {
|
||||
t.add_export_zip_task(d.gid, d.output);
|
||||
} else if (d.type == "task_list") {
|
||||
t.get_task_list().then((tasks) => {
|
||||
sendMessage({
|
||||
type: "tasks",
|
||||
tasks,
|
||||
running: t.get_running_task(),
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
null;
|
||||
|
||||
18
server/task.ts
Normal file
18
server/task.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Task } from "../task.ts";
|
||||
import { TaskEventData } from "../task_manager.ts";
|
||||
import { DiscriminatedUnion } from "../utils.ts";
|
||||
|
||||
export type TaskServerSocketData = TaskEventData | { type: "close" } | {
|
||||
type: "tasks";
|
||||
tasks: Task[];
|
||||
running: number[];
|
||||
};
|
||||
|
||||
type EventMap = {
|
||||
new_download_task: { gid: number; token: string };
|
||||
new_export_zip_task: { gid: number; output?: string };
|
||||
};
|
||||
|
||||
export type TaskClientSocketData = DiscriminatedUnion<"type", EventMap> | {
|
||||
type: "close";
|
||||
} | { type: "task_list" };
|
||||
4
server/utils.ts
Normal file
4
server/utils.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export function get_ws_host() {
|
||||
const protocol = document.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
return `${protocol}//${document.location.host}`;
|
||||
}
|
||||
22
task.ts
22
task.ts
@@ -5,9 +5,9 @@ export enum TaskType {
|
||||
ExportZip,
|
||||
}
|
||||
|
||||
export type Task = {
|
||||
export type Task<T extends TaskType = TaskType> = {
|
||||
id: number;
|
||||
type: TaskType;
|
||||
type: T;
|
||||
gid: number;
|
||||
token: string;
|
||||
pid: number;
|
||||
@@ -30,9 +30,23 @@ type TaskId<T extends Record<PropertyKey, unknown>> = {
|
||||
} & T[P]) extends infer U ? { [Q in keyof U]: U[Q] } : never;
|
||||
};
|
||||
|
||||
export type TaskProgressType = TaskId<{
|
||||
export type TaskProgressBasicType = {
|
||||
[TaskType.Download]: TaskDownloadProgess;
|
||||
[TaskType.ExportZip]: TaskExportZipProgress;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type TaskProgressType = TaskId<TaskProgressBasicType>;
|
||||
|
||||
export type TaskProgress = DiscriminatedUnion<"type", TaskProgressType>;
|
||||
|
||||
export enum TaskStatus {
|
||||
Wait,
|
||||
Running,
|
||||
Finished,
|
||||
}
|
||||
|
||||
export type TaskDetail<T extends TaskType = TaskType> = {
|
||||
base: Task<T>;
|
||||
progress?: TaskProgressBasicType[T];
|
||||
status: TaskStatus;
|
||||
};
|
||||
|
||||
@@ -33,12 +33,17 @@ type Detail<T extends Record<PropertyKey, unknown>> = {
|
||||
|
||||
export type TaskEventData = DiscriminatedUnion<"type", Detail<EventMap>>;
|
||||
|
||||
type RunningTask = {
|
||||
task: Promise<Task>;
|
||||
base: Task;
|
||||
};
|
||||
|
||||
export class TaskManager extends EventTarget {
|
||||
#closed = false;
|
||||
cfg;
|
||||
client;
|
||||
db;
|
||||
running_tasks: Map<number, Promise<Task>>;
|
||||
running_tasks: Map<number, RunningTask>;
|
||||
max_task_count;
|
||||
#abort;
|
||||
#force_abort;
|
||||
@@ -131,7 +136,7 @@ export class TaskManager extends EventTarget {
|
||||
this.#check_closed();
|
||||
const removed_task: number[] = [];
|
||||
for (const [id, task] of this.running_tasks) {
|
||||
const status = await promiseState(task);
|
||||
const status = await promiseState(task.task);
|
||||
if (status.status == PromiseStatus.Fulfilled && status.value) {
|
||||
removed_task.push(id);
|
||||
await this.db.delete_task(status.value);
|
||||
@@ -175,6 +180,12 @@ export class TaskManager extends EventTarget {
|
||||
get force_aborts() {
|
||||
return this.#force_abort.signal;
|
||||
}
|
||||
get_running_task() {
|
||||
return Array.from(this.running_tasks.keys());
|
||||
}
|
||||
get_task_list() {
|
||||
return this.db.get_tasks();
|
||||
}
|
||||
// @ts-ignore Checked type
|
||||
removeEventListener<T extends keyof EventMap>(
|
||||
type: T,
|
||||
@@ -221,15 +232,18 @@ export class TaskManager extends EventTarget {
|
||||
if (task.type == TaskType.Download) {
|
||||
this.running_tasks.set(
|
||||
task.id,
|
||||
download_task(
|
||||
task,
|
||||
this.client,
|
||||
this.db,
|
||||
this.cfg,
|
||||
this.#abort.signal,
|
||||
this.#force_abort.signal,
|
||||
this,
|
||||
),
|
||||
{
|
||||
task: download_task(
|
||||
task,
|
||||
this.client,
|
||||
this.db,
|
||||
this.cfg,
|
||||
this.#abort.signal,
|
||||
this.#force_abort.signal,
|
||||
this,
|
||||
),
|
||||
base: task,
|
||||
},
|
||||
);
|
||||
} else if (task.type == TaskType.ExportZip) {
|
||||
const cfg: ExportZipConfig = task.details
|
||||
@@ -237,14 +251,17 @@ export class TaskManager extends EventTarget {
|
||||
: DEFAULT_EXPORT_ZIP_CONFIG;
|
||||
this.running_tasks.set(
|
||||
task.id,
|
||||
export_zip(
|
||||
task,
|
||||
this.db,
|
||||
this.cfg,
|
||||
this.#abort.signal,
|
||||
cfg,
|
||||
this,
|
||||
),
|
||||
{
|
||||
task: export_zip(
|
||||
task,
|
||||
this.db,
|
||||
this.cfg,
|
||||
this.#abort.signal,
|
||||
cfg,
|
||||
this,
|
||||
),
|
||||
base: task,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user