Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5461e74539 | |||
| 417484cb6d | |||
| 9aaffd3550 | |||
| 94759d48c7 | |||
| c0e343b696 | |||
| f18ac7c444 | |||
| f637610734 | |||
| 5e1046dd5b | |||
| f25584cb2f | |||
| 03999585d0 | |||
| 772c2b0300 | |||
| 3622e67a56 | |||
| c51630cdd6 | |||
| dab1c55a93 | |||
| 7b966bc988 | |||
| e5e46bee38 | |||
| 3365d96a03 | |||
| fb223cd493 | |||
| 60d49c4773 | |||
| e7aa4ae147 | |||
| c45be41ddc | |||
| 85b37facee | |||
| 90567009ef | |||
| 899afc156e | |||
| 9e33b7e488 | |||
| e7ba60a51f | |||
| 2a3ad325c2 | |||
| 7575b80d31 | |||
| 8bb9d119b5 | |||
| 5da3f2b1c7 | |||
| da6cbcfb75 | |||
| 3d7aff41ee | |||
| 2cc49e8679 | |||
| 0e961c0647 | |||
| 4d5757a6ce | |||
| 62ae0e5075 | |||
| 7513fe2882 | |||
| c945dc8360 | |||
| de7e0da045 |
@@ -2,6 +2,7 @@ acodec
|
||||
adblocker
|
||||
ahooks
|
||||
antd
|
||||
aplus
|
||||
behaviour
|
||||
bili
|
||||
bilibili
|
||||
@@ -15,6 +16,7 @@ conventionalcommits
|
||||
datetime
|
||||
DDTHH
|
||||
execa
|
||||
idtype
|
||||
immer
|
||||
inversify
|
||||
KHTML
|
||||
@@ -34,7 +36,9 @@ svgz
|
||||
TDAPP
|
||||
tiptap
|
||||
typeorm
|
||||
vitepress
|
||||
vuedraggable
|
||||
vuejs
|
||||
waline
|
||||
watting
|
||||
Watting
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
APP_NAME=mediago
|
||||
APP_ID=mediago.ziying.site
|
||||
APP_COPYRIGHT=caorushizi
|
||||
APP_VERSION=2.2.1
|
||||
APP_VERSION=2.2.3
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
APP_TD_APPID=
|
||||
APP_CLARITY_APPID=
|
||||
|
||||
GH_TOKEN=
|
||||
LOAD_DEVTOOLS=
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
APP_TD_APPID=
|
||||
APP_CLARITY_APPID=
|
||||
|
||||
GH_TOKEN=
|
||||
DEBUG_PLUGINS=false
|
||||
|
||||
+5
-1
@@ -8,7 +8,11 @@ outline: deep
|
||||
这篇文章是软件的简单说明,可以快速上手使用本本软件。
|
||||
|
||||
::: tip
|
||||
我建了一个 QQ 群,方便大家一起沟通交流: 574209001
|
||||
方便大家一起沟通交流, 可以加入 QQ 反馈群:
|
||||
|
||||
MediaGo 反馈群 1: 574209001
|
||||
|
||||
MediaGo 反馈群 2: 921619626
|
||||
:::
|
||||
|
||||
## 下载安装
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mediago",
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.3",
|
||||
"description": "在线视频下载器",
|
||||
"main": "main/index.js",
|
||||
"author": "caorushizi",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"typescript-eslint": "^7.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cliqz/adblocker-electron": "^1.27.3",
|
||||
"@cliqz/adblocker-electron-preload": "^1.27.3",
|
||||
"@cliqz/adblocker-electron": "1.27.3",
|
||||
"@cliqz/adblocker-electron-preload": "1.27.3",
|
||||
"axios": "^1.7.2",
|
||||
"better-sqlite3": "^10.0.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
@@ -66,7 +66,9 @@
|
||||
"lint-staged": "^15.2.5",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.35",
|
||||
"nanoid": "^5.0.7",
|
||||
"node-fetch": "^3.3.2",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"node-pty": "^1.0.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"strip-ansi": "^7.1.0",
|
||||
|
||||
@@ -39,7 +39,7 @@ function getConfig(): esbuild.BuildOptions {
|
||||
function buildOptions(
|
||||
entry: string,
|
||||
platform: esbuild.Platform,
|
||||
target: string,
|
||||
target: string
|
||||
): esbuild.BuildOptions {
|
||||
return {
|
||||
...getConfig(),
|
||||
@@ -111,10 +111,12 @@ export function getReleaseConfig(): Configuration {
|
||||
},
|
||||
mac: {
|
||||
icon: "../assets/icon.icns",
|
||||
target: {
|
||||
target: "dmg",
|
||||
arch: ["x64"],
|
||||
},
|
||||
target: [
|
||||
{
|
||||
target: "dmg",
|
||||
arch: ["x64", "arm64"],
|
||||
},
|
||||
],
|
||||
},
|
||||
linux: {
|
||||
category: "Utility",
|
||||
@@ -126,17 +128,11 @@ export function getReleaseConfig(): Configuration {
|
||||
},
|
||||
},
|
||||
nsis: {
|
||||
oneClick: true,
|
||||
oneClick: false,
|
||||
allowElevation: true,
|
||||
allowToChangeInstallationDirectory: false,
|
||||
installerIcon: "",
|
||||
uninstallerIcon: "",
|
||||
installerHeaderIcon: "",
|
||||
allowToChangeInstallationDirectory: true,
|
||||
createDesktopShortcut: true,
|
||||
createStartMenuShortcut: true,
|
||||
shortcutName: "",
|
||||
include: "",
|
||||
script: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ async function pack() {
|
||||
provider: "github",
|
||||
repo: "mediago",
|
||||
owner: "caorushizi",
|
||||
releaseType: "prerelease",
|
||||
releaseType: "draft",
|
||||
};
|
||||
}
|
||||
await builder.build({ config });
|
||||
|
||||
@@ -34,12 +34,11 @@ export default class ElectronApp {
|
||||
@inject(TYPES.ElectronDevtools)
|
||||
private readonly devTools: ElectronDevtools,
|
||||
@inject(TYPES.ElectronStore)
|
||||
private readonly store: ElectronStore,
|
||||
private readonly store: ElectronStore
|
||||
) {}
|
||||
|
||||
private async serviceInit(): Promise<void> {
|
||||
this.mainWindow.init();
|
||||
this.webview.init();
|
||||
}
|
||||
|
||||
private async vendorInit() {
|
||||
@@ -94,7 +93,7 @@ export default class ElectronApp {
|
||||
const videoIds = videos.map((video) => video.id);
|
||||
await this.videoRepository.changeVideoStatus(
|
||||
videoIds,
|
||||
DownloadStatus.Failed,
|
||||
DownloadStatus.Failed
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import WebviewService from "../services/WebviewService.ts";
|
||||
import FavoriteRepository from "../repository/FavoriteRepository.ts";
|
||||
import VideoRepository from "../repository/VideoRepository.ts";
|
||||
import ConversionRepository from "../repository/ConversionRepository.ts";
|
||||
import { machineId } from "node-machine-id";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
@injectable()
|
||||
export default class HomeController implements Controller {
|
||||
@@ -146,6 +148,10 @@ export default class HomeController implements Controller {
|
||||
if (key === "isMobile") {
|
||||
this.webviewService.setUserAgent(val);
|
||||
}
|
||||
// privacy
|
||||
if (key === "privacy") {
|
||||
this.webviewService.setDefaultSession(val);
|
||||
}
|
||||
|
||||
this.store.set(key, val);
|
||||
}
|
||||
@@ -271,4 +277,21 @@ export default class HomeController implements Controller {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@handle("get-machine-id")
|
||||
async getMachineId() {
|
||||
try {
|
||||
const id = this.store.get("machineId");
|
||||
if (id) return id;
|
||||
const newId = await machineId();
|
||||
this.store.set("machineId", newId);
|
||||
return newId;
|
||||
} catch (e) {
|
||||
const id = this.store.get("machineId");
|
||||
if (id) return id;
|
||||
const newId = nanoid();
|
||||
this.store.set("machineId", newId);
|
||||
return newId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class WebviewController implements Controller {
|
||||
@inject(TYPES.ElectronStore)
|
||||
private readonly store: ElectronStore,
|
||||
@inject(TYPES.SniffingHelper)
|
||||
private readonly sniffingHelper: SniffingHelper,
|
||||
private readonly sniffingHelper: SniffingHelper
|
||||
) {}
|
||||
|
||||
@handle("set-webview-bounds")
|
||||
@@ -29,7 +29,7 @@ export default class WebviewController implements Controller {
|
||||
}
|
||||
|
||||
@handle("webview-load-url")
|
||||
async browserViewLoadUrl(e: IpcMainEvent, url?: string): Promise<void> {
|
||||
async browserViewLoadUrl(e: IpcMainEvent, url: string): Promise<void> {
|
||||
await this.webview.loadURL(url);
|
||||
}
|
||||
|
||||
@@ -85,4 +85,9 @@ export default class WebviewController implements Controller {
|
||||
async pluginReady() {
|
||||
this.sniffingHelper.pluginReady();
|
||||
}
|
||||
|
||||
@handle("clear-webview-cache")
|
||||
async clearWebviewCache() {
|
||||
return this.webview.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ export const workspace = resolve(appData, appName);
|
||||
export const defaultScheme = "mediago";
|
||||
export const PERSIST_MEDIAGO = "persist:mediago";
|
||||
export const PERSIST_WEBVIEW = "persist:webview";
|
||||
export const PRIVACY_WEBVIEW = "webview";
|
||||
export const db = resolve(workspace, "app.db");
|
||||
|
||||
// bin path
|
||||
|
||||
Vendored
+6
@@ -52,6 +52,12 @@ declare interface AppStore {
|
||||
showTerminal: boolean;
|
||||
// 隐私模式
|
||||
privacy: boolean;
|
||||
// 机器id
|
||||
machineId: string;
|
||||
// 下载代理设置
|
||||
downloadProxySwitch: boolean;
|
||||
// 自动更新
|
||||
autoUpgrade: boolean;
|
||||
}
|
||||
|
||||
declare interface BrowserStore {
|
||||
|
||||
@@ -60,7 +60,8 @@ const electronApi = {
|
||||
): Promise<void> {
|
||||
return ipcRenderer.invoke("set-app-store", key, val);
|
||||
},
|
||||
openDir(dir: string): Promise<void> {
|
||||
async openDir(dir?: string): Promise<void> {
|
||||
if (!dir) return;
|
||||
return ipcRenderer.invoke("open-dir", dir);
|
||||
},
|
||||
addDownloadItem(video: Omit<DownloadItem, "id">): Promise<Video> {
|
||||
@@ -169,6 +170,12 @@ const electronApi = {
|
||||
deleteConversion(id: number): Promise<void> {
|
||||
return ipcRenderer.invoke("delete-conversion", id);
|
||||
},
|
||||
getMachineId(): Promise<string> {
|
||||
return ipcRenderer.invoke("get-machine-id");
|
||||
},
|
||||
clearWebviewCache(): Promise<void> {
|
||||
return ipcRenderer.invoke("clear-webview-cache");
|
||||
},
|
||||
};
|
||||
|
||||
contextBridge.exposeInMainWorld(apiKey, electronApi);
|
||||
|
||||
@@ -74,6 +74,9 @@ const processList: Schema[] = [
|
||||
proxy: {
|
||||
argsName: ["--custom-proxy"],
|
||||
},
|
||||
__common__: {
|
||||
argsName: ["--no-log"],
|
||||
},
|
||||
},
|
||||
consoleReg: {
|
||||
percent: "([\\d.]+)%",
|
||||
@@ -121,7 +124,7 @@ export default class DownloadService extends EventEmitter {
|
||||
@inject(TYPES.VideoRepository)
|
||||
private readonly videoRepository: VideoRepository,
|
||||
@inject(TYPES.ElectronStore)
|
||||
private readonly storeService: ElectronStore,
|
||||
private readonly storeService: ElectronStore
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -148,7 +151,7 @@ export default class DownloadService extends EventEmitter {
|
||||
try {
|
||||
await this.videoRepository.changeVideoStatus(
|
||||
task.id,
|
||||
DownloadStatus.Downloading,
|
||||
DownloadStatus.Downloading
|
||||
);
|
||||
this.emit("download-start", task.id);
|
||||
|
||||
@@ -174,8 +177,8 @@ export default class DownloadService extends EventEmitter {
|
||||
callback,
|
||||
};
|
||||
|
||||
const { proxy, useProxy } = this.storeService.store;
|
||||
if (useProxy) {
|
||||
const { proxy, downloadProxySwitch } = this.storeService.store;
|
||||
if (downloadProxySwitch && proxy) {
|
||||
params.proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -185,7 +188,7 @@ export default class DownloadService extends EventEmitter {
|
||||
|
||||
await this.videoRepository.changeVideoStatus(
|
||||
task.id,
|
||||
DownloadStatus.Success,
|
||||
DownloadStatus.Success
|
||||
);
|
||||
this.emit("download-success", task.id);
|
||||
} catch (err: any) {
|
||||
@@ -194,7 +197,7 @@ export default class DownloadService extends EventEmitter {
|
||||
// 下载暂停
|
||||
await this.videoRepository.changeVideoStatus(
|
||||
task.id,
|
||||
DownloadStatus.Stopped,
|
||||
DownloadStatus.Stopped
|
||||
);
|
||||
this.emit("download-stop", task.id);
|
||||
} else {
|
||||
@@ -202,7 +205,7 @@ export default class DownloadService extends EventEmitter {
|
||||
// 下载失败
|
||||
await this.videoRepository.changeVideoStatus(
|
||||
task.id,
|
||||
DownloadStatus.Failed,
|
||||
DownloadStatus.Failed
|
||||
);
|
||||
this.emit("download-failed", task.id, err);
|
||||
}
|
||||
@@ -239,7 +242,7 @@ export default class DownloadService extends EventEmitter {
|
||||
private _execa(
|
||||
binPath: string,
|
||||
args: string[],
|
||||
params: DownloadOptions,
|
||||
params: DownloadOptions
|
||||
): Promise<void> {
|
||||
const { abortSignal, onMessage, id } = params;
|
||||
|
||||
@@ -326,6 +329,10 @@ export default class DownloadService extends EventEmitter {
|
||||
if (key === "proxy" && proxy) {
|
||||
argsName && argsName.forEach((i) => spawnParams.push(i, proxy));
|
||||
}
|
||||
|
||||
if (key === "__common__") {
|
||||
argsName && spawnParams.push(...argsName);
|
||||
}
|
||||
}
|
||||
|
||||
const { consoleReg } = schema;
|
||||
|
||||
@@ -4,7 +4,11 @@ import { TYPES } from "../types.ts";
|
||||
import ElectronLogger from "../vendor/ElectronLogger.ts";
|
||||
import EventEmitter from "events";
|
||||
import { OnSendHeadersListenerDetails, session } from "electron";
|
||||
import { PERSIST_WEBVIEW, formatHeaders } from "../helper/index.ts";
|
||||
import {
|
||||
PERSIST_WEBVIEW,
|
||||
PRIVACY_WEBVIEW,
|
||||
formatHeaders,
|
||||
} from "../helper/index.ts";
|
||||
|
||||
export interface SourceParams {
|
||||
url: string;
|
||||
@@ -61,6 +65,10 @@ export class SniffingHelper extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
update(pageInfo: PageInfo) {
|
||||
this.pageInfo = pageInfo;
|
||||
}
|
||||
|
||||
reset(pageInfo: PageInfo) {
|
||||
this.pageInfo = pageInfo;
|
||||
this.ready = false;
|
||||
@@ -85,8 +93,9 @@ export class SniffingHelper extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
const viewSession = session.fromPartition(PERSIST_WEBVIEW);
|
||||
start(privacy: boolean = false) {
|
||||
const partition = privacy ? PRIVACY_WEBVIEW : PERSIST_WEBVIEW;
|
||||
const viewSession = session.fromPartition(partition);
|
||||
viewSession.webRequest.onSendHeaders(this.onSendHeaders);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { WebContentsView, session } from "electron";
|
||||
import { Event, HandlerDetails, WebContentsView, session } from "electron";
|
||||
import { inject, injectable } from "inversify";
|
||||
import { TYPES } from "../types.ts";
|
||||
import isDev from "electron-is-dev";
|
||||
import {
|
||||
PERSIST_WEBVIEW,
|
||||
PRIVACY_WEBVIEW,
|
||||
fetch,
|
||||
mobileUA,
|
||||
pcUA,
|
||||
@@ -15,14 +16,15 @@ import ElectronStore from "../vendor/ElectronStore.ts";
|
||||
import MainWindow from "../windows/MainWindow.ts";
|
||||
import BrowserWindow from "../windows/BrowserWindow.ts";
|
||||
import VideoRepository from "../repository/VideoRepository.ts";
|
||||
import { SniffingHelper } from "./SniffingHelperService.ts";
|
||||
import { SniffingHelper, SourceParams } from "./SniffingHelperService.ts";
|
||||
import { resolve } from "path";
|
||||
import { readFileSync } from "fs-extra";
|
||||
|
||||
@injectable()
|
||||
export default class WebviewService {
|
||||
public view: WebContentsView;
|
||||
private view: WebContentsView | null = null;
|
||||
private blocker?: ElectronBlocker;
|
||||
private defaultSession: string;
|
||||
|
||||
constructor(
|
||||
@inject(TYPES.MainWindow)
|
||||
@@ -36,129 +38,182 @@ export default class WebviewService {
|
||||
@inject(TYPES.VideoRepository)
|
||||
private readonly videoRepository: VideoRepository,
|
||||
@inject(TYPES.SniffingHelper)
|
||||
private readonly sniffingHelper: SniffingHelper,
|
||||
private readonly sniffingHelper: SniffingHelper
|
||||
) {
|
||||
// 初始化 blocker
|
||||
this.initBlocker();
|
||||
|
||||
const { useProxy, proxy, privacy } = this.store.store;
|
||||
|
||||
this.sniffingHelper.start(privacy);
|
||||
this.sniffingHelper.on("source", this.onSource);
|
||||
|
||||
this.setDefaultSession(privacy, true);
|
||||
this.setProxy(useProxy, proxy);
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
this.view = new WebContentsView({
|
||||
webPreferences: {
|
||||
partition: PERSIST_WEBVIEW,
|
||||
partition: this.defaultSession,
|
||||
preload: resolve(__dirname, "./preload.js"),
|
||||
},
|
||||
});
|
||||
this.view.setBackgroundColor("#fff");
|
||||
this.webContents.setAudioMuted(true);
|
||||
this.view.webContents.setAudioMuted(true);
|
||||
|
||||
const { useProxy, proxy, isMobile } = this.store.store;
|
||||
this.setProxy(useProxy, proxy);
|
||||
const { isMobile } = this.store.store;
|
||||
this.setUserAgent(isMobile);
|
||||
|
||||
this.webContents.on("dom-ready", async () => {
|
||||
const baseInfo = {
|
||||
title: this.webContents.getTitle(),
|
||||
url: this.webContents.getURL(),
|
||||
};
|
||||
this.sniffingHelper.reset(baseInfo);
|
||||
this.window.webContents.send("webview-dom-ready", baseInfo);
|
||||
this.view.webContents.on("dom-ready", this.onDomReady);
|
||||
this.view.webContents.on("did-navigate", this.onDidNavigate);
|
||||
this.view.webContents.on("did-fail-load", this.onDidFailLoad);
|
||||
this.view.webContents.on("did-navigate-in-page", this.onDidNavigateInPage);
|
||||
this.view.webContents.setWindowOpenHandler(this.onOpenNewWindow);
|
||||
}
|
||||
|
||||
try {
|
||||
if (isDev && process.env.DEBUG_PLUGINS === "true") {
|
||||
const content =
|
||||
'function addScript(src){const script=document.createElement("script");script.src=src;script.type="module";document.body.appendChild(script)}addScript("http://localhost:8080/src/main.ts");';
|
||||
await this.webContents.executeJavaScript(content);
|
||||
} else {
|
||||
const content = readFileSync(pluginPath, "utf-8");
|
||||
await this.webContents.executeJavaScript(content);
|
||||
}
|
||||
} catch (err) {
|
||||
// empty
|
||||
}
|
||||
});
|
||||
onDomReady = () => {
|
||||
if (!this.view) return;
|
||||
const pageInfo = this.getPageInfo();
|
||||
this.sniffingHelper.update(pageInfo);
|
||||
this.window.webContents.send("webview-dom-ready", pageInfo);
|
||||
};
|
||||
|
||||
// 兼容网站在当前页面中打开
|
||||
this.webContents.setWindowOpenHandler(({ url }) => {
|
||||
this.loadURL(url, true);
|
||||
onDidNavigate = async () => {
|
||||
if (!this.view) return;
|
||||
const pageInfo = this.getPageInfo();
|
||||
this.sniffingHelper.reset(pageInfo);
|
||||
this.window.webContents.send("webview-did-navigate", pageInfo);
|
||||
|
||||
return { action: "deny" };
|
||||
});
|
||||
|
||||
this.sniffingHelper.start();
|
||||
this.sniffingHelper.on("source", async (item) => {
|
||||
// 这里需要判断是否使用浏览器插件
|
||||
const useExtension = this.store.get("useExtension");
|
||||
if (useExtension) {
|
||||
this.webContents.send("webview-link-message", item);
|
||||
try {
|
||||
if (isDev && process.env.DEBUG_PLUGINS === "true") {
|
||||
const content =
|
||||
'function addScript(src){const script=document.createElement("script");script.src=src;script.type="module";document.body.appendChild(script)}addScript("http://localhost:8080/src/main.ts");';
|
||||
await this.view.webContents.executeJavaScript(content);
|
||||
} else {
|
||||
const video = await this.videoRepository.findVideoByName(item.name);
|
||||
if (video) {
|
||||
item.name = `${item.name}-${Date.now()}`;
|
||||
}
|
||||
const res = await this.videoRepository.addVideo(item);
|
||||
const mainWebContents = this.mainWindow.window?.webContents;
|
||||
if (!mainWebContents) return;
|
||||
// 这里向页面发送消息,通知页面更新
|
||||
mainWebContents.send("download-item-notifier", res);
|
||||
const content = readFileSync(pluginPath, "utf-8");
|
||||
await this.view.webContents.executeJavaScript(content);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
||||
onDidFailLoad = (e: Event, code: number, desc: string) => {
|
||||
this.window.webContents.send("webview-fail-load", { code, desc });
|
||||
};
|
||||
|
||||
onDidNavigateInPage = () => {
|
||||
if (!this.view) return;
|
||||
const pageInfo = this.getPageInfo();
|
||||
this.sniffingHelper.update(pageInfo);
|
||||
this.window.webContents.send("webview-did-navigate-in-page", pageInfo);
|
||||
};
|
||||
|
||||
onOpenNewWindow = ({ url }: HandlerDetails) => {
|
||||
this.loadURL(url);
|
||||
|
||||
return { action: "deny" } as { action: "deny" };
|
||||
};
|
||||
|
||||
onSource = async (item: SourceParams) => {
|
||||
if (!this.view) return;
|
||||
// 这里需要判断是否使用浏览器插件
|
||||
const useExtension = this.store.get("useExtension");
|
||||
if (useExtension) {
|
||||
this.view.webContents.send("webview-link-message", item);
|
||||
} else {
|
||||
const video = await this.videoRepository.findVideoByName(item.name);
|
||||
if (video) {
|
||||
item.name = `${item.name}-${Date.now()}`;
|
||||
}
|
||||
const res = await this.videoRepository.addVideo(item);
|
||||
const mainWebContents = this.mainWindow.window?.webContents;
|
||||
if (!mainWebContents) return;
|
||||
// 这里向页面发送消息,通知页面更新
|
||||
mainWebContents.send("download-item-notifier", res);
|
||||
}
|
||||
};
|
||||
|
||||
getPageInfo() {
|
||||
if (!this.view) {
|
||||
throw new Error("未找到 view");
|
||||
}
|
||||
return {
|
||||
title: this.view.webContents.getTitle(),
|
||||
url: this.view.webContents.getURL(),
|
||||
};
|
||||
}
|
||||
|
||||
getBounds(): Electron.Rectangle {
|
||||
if (!this.view) {
|
||||
throw new Error("未找到 view");
|
||||
}
|
||||
return this.view.getBounds();
|
||||
}
|
||||
|
||||
setBounds(bounds: Electron.Rectangle): void {
|
||||
if (!this.view) return;
|
||||
this.view.setBounds(bounds);
|
||||
}
|
||||
|
||||
setBackgroundColor(color: string): void {
|
||||
if (!this.view) return;
|
||||
this.view.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
show() {
|
||||
if (!this.view) return;
|
||||
this.window.contentView.addChildView(this.view);
|
||||
isDev && this.webContents.openDevTools();
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (!this.view) return;
|
||||
this.window.contentView.removeChildView(this.view);
|
||||
isDev && this.webContents.closeDevTools();
|
||||
}
|
||||
|
||||
setBounds(bounds: Electron.Rectangle): void {
|
||||
this.view.setBounds(bounds);
|
||||
}
|
||||
|
||||
async loadURL(url?: string, isNewWindow?: boolean) {
|
||||
const canGoBack = this.webContents.canGoBack();
|
||||
|
||||
try {
|
||||
this.webContents.stop();
|
||||
await this.webContents.loadURL(url || "");
|
||||
} catch (err: unknown) {
|
||||
this.logger.error("加载 url 时出现错误: ", err);
|
||||
throw err;
|
||||
} finally {
|
||||
if (!canGoBack && !isNewWindow) {
|
||||
this.webContents.clearHistory();
|
||||
}
|
||||
loadURL(url: string) {
|
||||
// 开始加载 url
|
||||
if (!this.view) {
|
||||
this.init();
|
||||
}
|
||||
if (!this.view) return;
|
||||
|
||||
// 1. 停止当前导航
|
||||
this.view.webContents.stop();
|
||||
|
||||
// 2. 加载新的 url
|
||||
this.view.webContents.loadURL(url);
|
||||
}
|
||||
|
||||
async goBack() {
|
||||
if (this.webContents.canGoBack()) {
|
||||
this.webContents.goBack();
|
||||
if (!this.view) {
|
||||
throw new Error("未找到 view");
|
||||
}
|
||||
const { webContents } = this.view;
|
||||
if (webContents.canGoBack()) {
|
||||
webContents.goBack();
|
||||
return true;
|
||||
} else {
|
||||
this.destroyView();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async reload() {
|
||||
this.webContents.reload();
|
||||
if (!this.view) {
|
||||
throw new Error("未找到 view");
|
||||
}
|
||||
this.view.webContents.reload();
|
||||
}
|
||||
|
||||
async goHome() {
|
||||
this.webContents.stop();
|
||||
this.webContents.clearHistory();
|
||||
if (!this.view) {
|
||||
throw new Error("未找到 view");
|
||||
}
|
||||
this.view.webContents.stop();
|
||||
this.view.webContents.clearHistory();
|
||||
this.destroyView();
|
||||
}
|
||||
|
||||
get window() {
|
||||
@@ -168,12 +223,12 @@ export default class WebviewService {
|
||||
}
|
||||
|
||||
private get session() {
|
||||
return session.fromPartition(PERSIST_WEBVIEW);
|
||||
return session.fromPartition(this.defaultSession);
|
||||
}
|
||||
|
||||
private enableProxy(proxy: string) {
|
||||
if (!proxy) {
|
||||
this.logger.error("[proxy] 代理地址不能为空");
|
||||
this.logger.error("[Proxy] 代理地址不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,12 +238,12 @@ export default class WebviewService {
|
||||
}
|
||||
|
||||
this.session.setProxy({ proxyRules: proxy });
|
||||
this.logger.info(`[proxy] 代理开启成功,代理地址为${proxy}`);
|
||||
this.logger.info(`[Proxy] 代理开启(${proxy})`);
|
||||
}
|
||||
|
||||
private disableProxy() {
|
||||
this.session.setProxy({ proxyRules: "" });
|
||||
this.logger.info("[proxy] 代理关闭成功");
|
||||
this.logger.info("[Proxy] 代理关闭");
|
||||
}
|
||||
|
||||
setProxy(useProxy: boolean, proxy: string): void {
|
||||
@@ -216,47 +271,78 @@ export default class WebviewService {
|
||||
|
||||
private enableBlocking() {
|
||||
if (!this.blocker) {
|
||||
this.logger.error("开启 blocker 失败,未初始化");
|
||||
this.logger.error("[AdBlocker] 开启失败(未初始化)");
|
||||
return;
|
||||
}
|
||||
this.blocker.enableBlockingInSession(this.session);
|
||||
this.logger.info("开启 blocker 成功");
|
||||
this.logger.info("[AdBlocker] 开启");
|
||||
}
|
||||
|
||||
private disableBlocking() {
|
||||
if (!this.blocker) {
|
||||
this.logger.error("关闭 blocker 失败,未初始化");
|
||||
this.logger.error("[AdBlocker] 关闭失败(未初始化)");
|
||||
return;
|
||||
}
|
||||
if (!this.blocker.isBlockingEnabled(this.session)) {
|
||||
return;
|
||||
}
|
||||
this.blocker.disableBlockingInSession(this.session);
|
||||
this.logger.info("关闭 blocker 成功");
|
||||
this.logger.info("[AdBlocker] 关闭");
|
||||
}
|
||||
|
||||
setUserAgent(isMobile?: boolean) {
|
||||
if (!this.view) return;
|
||||
if (isMobile) {
|
||||
this.webContents.setUserAgent(mobileUA);
|
||||
this.view.webContents.setUserAgent(mobileUA);
|
||||
} else {
|
||||
this.webContents.setUserAgent(pcUA);
|
||||
this.view.webContents.setUserAgent(pcUA);
|
||||
}
|
||||
this.logger.info("设置 user-agent 成功", isMobile);
|
||||
}
|
||||
|
||||
get debugger() {
|
||||
return this.webContents.debugger;
|
||||
}
|
||||
|
||||
get webContents() {
|
||||
return this.view.webContents;
|
||||
this.logger.info(`[UA] 设置为${isMobile ? "移动端" : " pc 端"}`);
|
||||
}
|
||||
|
||||
captureView(): Promise<Electron.NativeImage> {
|
||||
if (!this.view) {
|
||||
throw new Error("未找到 view");
|
||||
}
|
||||
return this.view.webContents.capturePage();
|
||||
}
|
||||
|
||||
sendToWindow(channel: string, ...args: unknown[]) {
|
||||
this.window.webContents.send(channel, ...args);
|
||||
}
|
||||
|
||||
destroyView() {
|
||||
if (this.view && this.window) {
|
||||
this.view.webContents.close();
|
||||
this.window.contentView.removeChildView(this.view);
|
||||
}
|
||||
// FIXME: 为了避免内存泄漏,这里需要销毁 view
|
||||
this.view = null;
|
||||
}
|
||||
|
||||
async clearCache() {
|
||||
await this.session.clearCache();
|
||||
await this.session.clearStorageData();
|
||||
}
|
||||
|
||||
setDefaultSession(isPrivacy = false, init = false) {
|
||||
this.logger.info(`[Session] ${isPrivacy ? "隐私" : "正常"}模式`);
|
||||
if (isPrivacy) {
|
||||
this.defaultSession = PRIVACY_WEBVIEW;
|
||||
} else {
|
||||
this.defaultSession = PERSIST_WEBVIEW;
|
||||
}
|
||||
|
||||
if (this.view) {
|
||||
const { useProxy, proxy } = this.store.store;
|
||||
this.destroyView();
|
||||
this.init();
|
||||
this.setProxy(useProxy, proxy);
|
||||
this.sniffingHelper.start(isPrivacy);
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
this.window.webContents.send("change-privacy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -28,6 +28,9 @@ export default class StoreService extends Store<AppStore> implements Vendor {
|
||||
language: AppLanguage.System,
|
||||
showTerminal: false,
|
||||
privacy: false,
|
||||
machineId: "",
|
||||
downloadProxySwitch: false,
|
||||
autoUpgrade: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+8
-1
@@ -4,16 +4,23 @@ import { TYPES } from "../types.ts";
|
||||
import isDev from "electron-is-dev";
|
||||
import ElectronLogger from "./ElectronLogger.ts";
|
||||
import { Vendor } from "../core/vendor.ts";
|
||||
import ElectronStore from "electron-store";
|
||||
|
||||
@injectable()
|
||||
export default class UpdateService implements Vendor {
|
||||
constructor(
|
||||
@inject(TYPES.ElectronLogger) private readonly logger: ElectronLogger,
|
||||
@inject(TYPES.ElectronLogger)
|
||||
private readonly logger: ElectronLogger,
|
||||
@inject(TYPES.ElectronStore)
|
||||
private readonly store: ElectronStore
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
if (isDev) return;
|
||||
|
||||
const { autoUpgrade } = this.store.store;
|
||||
if (!autoUpgrade) return;
|
||||
|
||||
try {
|
||||
autoUpdater.disableWebInstaller = true;
|
||||
autoUpdater.logger = this.logger.logger;
|
||||
|
||||
@@ -115,7 +115,7 @@ export class FloatButton extends LitElement {
|
||||
};
|
||||
|
||||
receiveMessage = (_: unknown, data: SourceData) => {
|
||||
this.data = [...this.data, data];
|
||||
this.data = [...this.data, { ...data, name: document.title }];
|
||||
};
|
||||
|
||||
handleMouseStart = (event: MouseEvent) => {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": ["stylelint-config-standard", "stylelint-config-standard-scss"]
|
||||
"extends": ["stylelint-config-standard", "stylelint-config-standard-scss"],
|
||||
"rules": {
|
||||
"no-descending-specificity": null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@ import { ConfigProvider, theme } from "antd";
|
||||
import React, { FC, Suspense, lazy, useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||
import { setAppStore, increase } from "./store";
|
||||
import { setAppStore, increase, setBrowserStore, PageMode } from "./store";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import zhCN from "antd/locale/zh_CN";
|
||||
import "./App.scss";
|
||||
import useElectron from "./hooks/electron";
|
||||
import Loading from "./components/Loading";
|
||||
import { DownloadFilter } from "./types";
|
||||
import { tdApp } from "./utils";
|
||||
import { useAsyncEffect } from "ahooks";
|
||||
|
||||
const AppLayout = lazy(() => import("./layout/App"));
|
||||
const HomePage = lazy(() => import("./pages/HomePage"));
|
||||
@@ -23,7 +25,7 @@ function getAlgorithm(appTheme: "dark" | "light") {
|
||||
const App: FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [appTheme, setAppTheme] = React.useState<"dark" | "light">("light");
|
||||
const { addIpcListener, removeIpcListener } = useElectron();
|
||||
const { addIpcListener, removeIpcListener, getMachineId } = useElectron();
|
||||
|
||||
const themeChange = (event: MediaQueryListEvent) => {
|
||||
if (event.matches) {
|
||||
@@ -42,16 +44,27 @@ const App: FC = () => {
|
||||
dispatch(increase());
|
||||
};
|
||||
|
||||
const onChangePrivacy = () => {
|
||||
dispatch(setBrowserStore({ url: "", title: "", mode: PageMode.Default }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
addIpcListener("store-change", onAppStoreChange);
|
||||
addIpcListener("download-item-notifier", onReceiveDownloadItem);
|
||||
addIpcListener("change-privacy", onChangePrivacy);
|
||||
|
||||
return () => {
|
||||
removeIpcListener("store-change", onAppStoreChange);
|
||||
removeIpcListener("download-item-notifier", onReceiveDownloadItem);
|
||||
removeIpcListener("change-privacy", onChangePrivacy);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
const deviceId = await getMachineId();
|
||||
tdApp.onEvent("页面加载", { deviceId });
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const isDarkTheme = matchMedia("(prefers-color-scheme: dark)");
|
||||
isDarkTheme.addEventListener("change", themeChange);
|
||||
|
||||
@@ -75,7 +75,8 @@ i18n
|
||||
pleaseSelectLanguage: "Please select language",
|
||||
downloadPrompt: "Download Prompt",
|
||||
browserSetting: "Browser Setting",
|
||||
pleaseEnterProxy: "Please enter proxy",
|
||||
pleaseEnterProxy:
|
||||
"You need to enter the full address, for example: http://127.0.0.1:8000",
|
||||
proxySetting: "Proxy Setting",
|
||||
pleaseEnterProxyFirst: "Please enter proxy first",
|
||||
blockAds: "Block Ads",
|
||||
@@ -84,11 +85,15 @@ i18n
|
||||
deleteSegments: "Delete segments after download",
|
||||
moreAction: "More Action",
|
||||
configDir: "Config Directory",
|
||||
clearCache: "Clear Cache",
|
||||
clearCacheSuccess: "Clear Cache Success",
|
||||
clearCacheFailed: "Clear Cache Failed",
|
||||
binPath: "Executable Path",
|
||||
localDir: "Local Directory",
|
||||
currentVersion: "Current Version",
|
||||
selectFolder: "Select Folder",
|
||||
proxySwitch: "Proxy Switch",
|
||||
downloadProxySwitch: "Download Proxy Switch",
|
||||
proxyDescription:
|
||||
"The proxy will take effect on the built-in browser and download",
|
||||
useImmersiveSniffing: "Use immersive sniffing",
|
||||
@@ -121,6 +126,8 @@ Referer: http://www.example.com`,
|
||||
close: "Close",
|
||||
exportLog: "Export Log",
|
||||
showTerminal: "Show Console",
|
||||
autoUpgrade: "Auto Upgrade",
|
||||
autoUpgradeTooltip: "Take effect next time you start",
|
||||
consoleOutput: "Console",
|
||||
downloadNow: "Download",
|
||||
addToDownloadList: "Confirm",
|
||||
@@ -131,6 +138,8 @@ Referer: http://www.example.com`,
|
||||
canUseMouseWheelToAdjust: "Can use mouse wheel to adjust",
|
||||
openBrowser: "Open Browser",
|
||||
privacy: "Privacy Mode",
|
||||
privacyTooltip:
|
||||
"After turning on the switch, the browser will not save any data",
|
||||
converter: "Converter",
|
||||
addFile: "Add File",
|
||||
},
|
||||
@@ -193,7 +202,7 @@ Referer: http://www.example.com`,
|
||||
pleaseSelectLanguage: "请选择显示语言",
|
||||
downloadPrompt: "下载完成提示",
|
||||
browserSetting: "浏览器设置",
|
||||
pleaseEnterProxy: "请填写代理地址",
|
||||
pleaseEnterProxy: "需要填写完整的地址,例如:http://127.0.0.1:8000",
|
||||
proxySetting: "代理设置",
|
||||
pleaseEnterProxyFirst: "请先输入代理地址",
|
||||
blockAds: "开启广告过滤",
|
||||
@@ -202,11 +211,15 @@ Referer: http://www.example.com`,
|
||||
deleteSegments: "下载完成删除分片",
|
||||
moreAction: "更多操作",
|
||||
configDir: "配置文件目录",
|
||||
clearCache: "清除缓存",
|
||||
clearCacheSuccess: "清除缓存成功",
|
||||
clearCacheFailed: "清除缓存失败",
|
||||
binPath: "可执行程序目录",
|
||||
localDir: "本地存储目录",
|
||||
currentVersion: "当前版本",
|
||||
selectFolder: "选择文件夹",
|
||||
proxySwitch: "代理开关",
|
||||
downloadProxySwitch: "下载代理开关",
|
||||
proxyDescription: "该代理会对软件自带浏览器以及下载时生效",
|
||||
useImmersiveSniffing: "使用沉浸式嗅探",
|
||||
immersiveSniffingDescription:
|
||||
@@ -242,6 +255,8 @@ Referer: http://www.example.com`,
|
||||
close: "关闭",
|
||||
exportLog: "导出日志",
|
||||
showTerminal: "显示控制台",
|
||||
autoUpgrade: "自动更新",
|
||||
autoUpgradeTooltip: "下次启动时生效",
|
||||
consoleOutput: "控制台",
|
||||
downloadNow: "下载",
|
||||
addToDownloadList: "确认",
|
||||
@@ -252,6 +267,7 @@ Referer: http://www.example.com`,
|
||||
canUseMouseWheelToAdjust: "可以使用鼠标滚轮调整",
|
||||
openBrowser: "打开浏览器",
|
||||
privacy: "隐私模式",
|
||||
privacyTooltip: "打开开关后浏览器将不保存任何数据",
|
||||
converter: "格式转换",
|
||||
addFile: "添加文件",
|
||||
},
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, { FC, useState } from "react";
|
||||
import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import { Badge, Button, Layout, Menu, MenuProps, Flex } from "antd";
|
||||
import { Badge, Button, Layout, Menu, MenuProps, Flex, Typography } from "antd";
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
DownloadOutlined,
|
||||
ExportOutlined,
|
||||
EyeInvisibleOutlined,
|
||||
ProfileOutlined,
|
||||
QuestionCircleOutlined,
|
||||
SettingOutlined,
|
||||
@@ -21,8 +22,10 @@ import {
|
||||
import { useAsyncEffect } from "ahooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useStyles } from "./style";
|
||||
import classNames from "classnames";
|
||||
|
||||
const { Footer, Sider, Content } = Layout;
|
||||
const { Text } = Typography;
|
||||
|
||||
type MenuItem = Required<MenuProps>["items"][number];
|
||||
|
||||
@@ -45,7 +48,6 @@ const App: FC = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const [showExport, setShowExport] = useState(false);
|
||||
const count = useSelector(selectCount);
|
||||
const appStore = useSelector(selectAppStore);
|
||||
const { styles } = useStyles();
|
||||
@@ -89,38 +91,33 @@ const App: FC = () => {
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Link to="/source" className={styles.linkItem}>
|
||||
<Link
|
||||
to="/source"
|
||||
className={classNames([styles.linkItem, styles.extract])}
|
||||
>
|
||||
<ProfileOutlined />
|
||||
<span>{t("materialExtraction")}</span>
|
||||
{showExport && (
|
||||
<Button
|
||||
title={t("openInNewWindow")}
|
||||
type="text"
|
||||
style={{ marginLeft: "auto" }}
|
||||
icon={<ExportOutlined />}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
<Button
|
||||
title={t("openInNewWindow")}
|
||||
type="text"
|
||||
className={styles.hoverButton}
|
||||
icon={<ExportOutlined />}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
dispatch(setAppStore({ openInNewWindow: true }));
|
||||
if (location.pathname === "/source") {
|
||||
navigate("/");
|
||||
}
|
||||
// FIXME: 有可能 webview 还没有完全隐藏
|
||||
await ipcSetAppStore("openInNewWindow", true);
|
||||
await showBrowserWindow();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
dispatch(setAppStore({ openInNewWindow: true }));
|
||||
if (location.pathname === "/source") {
|
||||
navigate("/");
|
||||
}
|
||||
// FIXME: 有可能 webview 还没有完全隐藏
|
||||
await ipcSetAppStore("openInNewWindow", true);
|
||||
await showBrowserWindow();
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
),
|
||||
key: "source",
|
||||
onMouseEnter: () => {
|
||||
setShowExport(true);
|
||||
},
|
||||
onMouseLeave: () => {
|
||||
setShowExport(false);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: (
|
||||
@@ -169,6 +166,14 @@ const App: FC = () => {
|
||||
<Outlet />
|
||||
</Content>
|
||||
<Footer className={styles.containerFooter}>
|
||||
<Text>
|
||||
{appStore.privacy && (
|
||||
<>
|
||||
<EyeInvisibleOutlined /> 隐私模式
|
||||
</>
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
type={"link"}
|
||||
onClick={openHelpUrl}
|
||||
|
||||
@@ -1,26 +1,42 @@
|
||||
import { createStyles } from "antd-style";
|
||||
|
||||
export const useStyles = createStyles(({ css, token }) => ({
|
||||
container: css`
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
`,
|
||||
containerSider: css`
|
||||
border-right: ${token.colorBorderSecondary} solid 1px;
|
||||
`,
|
||||
containerInner: css`
|
||||
height: 100%;
|
||||
`,
|
||||
linkItem: css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
`,
|
||||
containerFooter: css`
|
||||
padding: 5px;
|
||||
background: ${token.colorBgContainer};
|
||||
text-align: right;
|
||||
border-top: ${token.colorBorderSecondary} solid 1px;
|
||||
`,
|
||||
}));
|
||||
export const useStyles = createStyles(({ css, token, cx }) => {
|
||||
const hoverButton = cx(css`
|
||||
margin-left: auto;
|
||||
display: none;
|
||||
`);
|
||||
return {
|
||||
container: css`
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
`,
|
||||
containerSider: css`
|
||||
border-right: ${token.colorBorderSecondary} solid 1px;
|
||||
`,
|
||||
containerInner: css`
|
||||
height: 100%;
|
||||
`,
|
||||
linkItem: css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
`,
|
||||
extract: css`
|
||||
&:hover {
|
||||
.${hoverButton} {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
`,
|
||||
containerFooter: css`
|
||||
padding: 5px 5px 5px 15px;
|
||||
background: ${token.colorBgContainer};
|
||||
text-align: right;
|
||||
border-top: ${token.colorBorderSecondary} solid 1px;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
`,
|
||||
hoverButton,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -5,18 +5,17 @@ import { Provider } from "react-redux";
|
||||
import store from "./store";
|
||||
import dayjs from "dayjs";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import { tdApp, initClarity } from "./utils";
|
||||
import { tdApp } from "./utils";
|
||||
import App from "./App";
|
||||
import "./i18n";
|
||||
|
||||
dayjs.locale("zh-cn");
|
||||
tdApp.init();
|
||||
initClarity();
|
||||
|
||||
createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<StrictMode>
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</StrictMode>,
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ const Converter = () => {
|
||||
);
|
||||
|
||||
const onClickConvertToAudio = async (item: Conversion) => {
|
||||
const nextState = produce(converting, (draft) => {
|
||||
const nextState = produce((draft) => {
|
||||
draft[item.id] = true;
|
||||
});
|
||||
setConverting(nextState);
|
||||
@@ -51,7 +51,7 @@ const Converter = () => {
|
||||
} catch (e: any) {
|
||||
messageApi.error(e.message);
|
||||
} finally {
|
||||
const nextState = produce(converting, (draft) => {
|
||||
const nextState = produce((draft) => {
|
||||
draft[item.id] = false;
|
||||
});
|
||||
setConverting(nextState);
|
||||
|
||||
@@ -78,7 +78,7 @@ const HomePage: FC<Props> = ({ filter = DownloadFilter.list }) => {
|
||||
});
|
||||
|
||||
const onDownloadProgress = (e: unknown, currProgress: DownloadProgress) => {
|
||||
const nextState = produce(progress, (draft) => {
|
||||
const nextState = produce((draft) => {
|
||||
draft[currProgress.id] = currProgress;
|
||||
});
|
||||
setProgress(nextState);
|
||||
|
||||
@@ -10,7 +10,11 @@ import {
|
||||
} from "@ant-design/pro-components";
|
||||
import "./index.scss";
|
||||
import { Button, FormInstance, message, Space, Tooltip } from "antd";
|
||||
import { FolderOpenOutlined, QuestionCircleOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
ClearOutlined,
|
||||
FolderOpenOutlined,
|
||||
QuestionCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { selectAppStore, setAppStore } from "../../store";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import useElectron from "../../hooks/electron";
|
||||
@@ -26,6 +30,7 @@ const SettingPage: React.FC = () => {
|
||||
setAppStore: ipcSetAppStore,
|
||||
getEnvPath,
|
||||
openDir,
|
||||
clearWebviewCache,
|
||||
} = useElectron();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
@@ -125,7 +130,17 @@ const SettingPage: React.FC = () => {
|
||||
<ProFormSwitch label={t("openInNewWindow")} name="openInNewWindow" />
|
||||
<ProFormSwitch label={t("downloadPrompt")} name="promptTone" />
|
||||
<ProFormSwitch label={t("showTerminal")} name="showTerminal" />
|
||||
{/* <ProFormSwitch label={t("privacy")} name="privacy" /> */}
|
||||
<ProFormSwitch
|
||||
label={renderTooltipLabel(
|
||||
t("autoUpgrade"),
|
||||
t("autoUpgradeTooltip")
|
||||
)}
|
||||
name="autoUpgrade"
|
||||
/>
|
||||
<ProFormSwitch
|
||||
label={renderTooltipLabel(t("privacy"), t("privacyTooltip"))}
|
||||
name="privacy"
|
||||
/>
|
||||
</ProFormGroup>
|
||||
<ProFormGroup title={t("browserSetting")} direction={"vertical"}>
|
||||
<ProFormText
|
||||
@@ -136,17 +151,16 @@ const SettingPage: React.FC = () => {
|
||||
/>
|
||||
<ProFormSwitch
|
||||
name="useProxy"
|
||||
label={renderTooltipLabel(t("proxySwitch"), t("proxyDescription"))}
|
||||
label={t("proxySwitch")}
|
||||
rules={[
|
||||
({ getFieldValue, setFieldValue }) => ({
|
||||
validator() {
|
||||
if (getFieldValue("proxy") === "") {
|
||||
setFieldValue("useProxy", false);
|
||||
{
|
||||
validator(rules, value) {
|
||||
if (value && formRef.current.getFieldValue("proxy") === "") {
|
||||
return Promise.reject(t("pleaseEnterProxyFirst"));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormSwitch label={t("blockAds")} name="blockAds" />
|
||||
@@ -154,17 +168,48 @@ const SettingPage: React.FC = () => {
|
||||
<ProFormSwitch
|
||||
label={renderTooltipLabel(
|
||||
t("useImmersiveSniffing"),
|
||||
t("immersiveSniffingDescription"),
|
||||
t("immersiveSniffingDescription")
|
||||
)}
|
||||
name="useExtension"
|
||||
/>
|
||||
<ProFormText label={t("moreAction")}>
|
||||
<Space>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
try {
|
||||
await clearWebviewCache();
|
||||
messageApi.success(t("clearCacheSuccess"));
|
||||
} catch (err) {
|
||||
messageApi.error(t("clearCacheFailed"));
|
||||
}
|
||||
}}
|
||||
icon={<ClearOutlined />}
|
||||
>
|
||||
{t("clearCache")}
|
||||
</Button>
|
||||
</Space>
|
||||
</ProFormText>
|
||||
</ProFormGroup>
|
||||
<ProFormGroup title={t("downloadSetting")} direction={"vertical"}>
|
||||
<ProFormSwitch
|
||||
name="downloadProxySwitch"
|
||||
label={t("downloadProxySwitch")}
|
||||
rules={[
|
||||
{
|
||||
validator(rules, value) {
|
||||
if (value && formRef.current.getFieldValue("proxy") === "") {
|
||||
return Promise.reject(t("pleaseEnterProxyFirst"));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormSwitch label={t("deleteSegments")} name="deleteSegments" />
|
||||
<ProFormDigit
|
||||
label={renderTooltipLabel(
|
||||
t("maxRunner"),
|
||||
t("maxRunnerDescription"),
|
||||
t("maxRunnerDescription")
|
||||
)}
|
||||
name="maxRunner"
|
||||
min={1}
|
||||
@@ -174,13 +219,13 @@ const SettingPage: React.FC = () => {
|
||||
<ProFormText label={t("moreAction")}>
|
||||
<Space>
|
||||
<Button
|
||||
onClick={() => envPath?.workspace && openDir(envPath.workspace)}
|
||||
onClick={() => openDir(envPath.workspace)}
|
||||
icon={<FolderOpenOutlined />}
|
||||
>
|
||||
{t("configDir")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => envPath?.binPath && openDir(envPath.binPath)}
|
||||
onClick={() => openDir(envPath.binPath)}
|
||||
icon={<FolderOpenOutlined />}
|
||||
>
|
||||
{t("binPath")}
|
||||
|
||||
@@ -105,9 +105,14 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 5px;
|
||||
transition: background 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: #f2f6fc;
|
||||
|
||||
.favorite-remove-btn {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -116,6 +121,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.favorite-remove-btn {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: -2px;
|
||||
color: gray;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
Space,
|
||||
Spin,
|
||||
} from "antd";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import PageContainer from "../../components/PageContainer";
|
||||
import useElectron from "../../hooks/electron";
|
||||
@@ -41,7 +41,6 @@ import {
|
||||
import WebView from "../../components/WebView";
|
||||
import { selectAppStore } from "../../store";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { nanoid } from "nanoid";
|
||||
import DownloadForm, { DownloadFormRef } from "../../components/DownloadForm";
|
||||
|
||||
interface SourceExtractProps {
|
||||
@@ -72,56 +71,36 @@ const SourceExtract: React.FC<SourceExtractProps> = ({ page = false }) => {
|
||||
const { data: favoriteList = [], refresh } = useRequest(getFavorites);
|
||||
const [favoriteAddForm] = Form.useForm<Favorite>();
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [hoverId, setHoverId] = useState<number>(-1);
|
||||
const store = useSelector(selectBrowserStore);
|
||||
const appStore = useSelector(selectAppStore);
|
||||
const [modalShow, setModalShow] = useState(false);
|
||||
const [placeHolder, setPlaceHolder] = useState<string>("");
|
||||
const sessionId = useRef("");
|
||||
const downloadForm = useRef<DownloadFormRef>(null);
|
||||
const originTitle = useRef(document.title);
|
||||
|
||||
const curIsFavorite = favoriteList.find((item) => item.url === store.url);
|
||||
const curIsFavorite = useMemo(() => {
|
||||
return favoriteList.find((item) => item.url === store.url);
|
||||
}, [favoriteList, store.url]);
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
const store = await ipcGetAppStore();
|
||||
dispatch(setAppStore(store));
|
||||
}, []);
|
||||
|
||||
const loadUrl = async (url: string) => {
|
||||
const id = nanoid();
|
||||
sessionId.current = id;
|
||||
try {
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
url: "",
|
||||
mode: PageMode.Browser,
|
||||
status: BrowserStatus.Loading,
|
||||
})
|
||||
);
|
||||
await webviewLoadURL(url);
|
||||
if (sessionId.current === id) {
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
url: url,
|
||||
status: BrowserStatus.Loaded,
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
if (sessionId.current === id) {
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
status: BrowserStatus.Failed,
|
||||
errMsg: (err as any).message,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
const loadUrl = (url: string) => {
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
url,
|
||||
mode: PageMode.Browser,
|
||||
status: BrowserStatus.Loading,
|
||||
})
|
||||
);
|
||||
webviewLoadURL(url);
|
||||
};
|
||||
|
||||
const goto = async () => {
|
||||
const goto = () => {
|
||||
const link = generateUrl(store.url);
|
||||
await loadUrl(link);
|
||||
loadUrl(link);
|
||||
};
|
||||
|
||||
const onInputKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
@@ -155,14 +134,9 @@ const SourceExtract: React.FC<SourceExtractProps> = ({ page = false }) => {
|
||||
|
||||
const onClickGoBack = async () => {
|
||||
const back = await webviewGoBack();
|
||||
|
||||
if (!back) {
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
url: "",
|
||||
mode: PageMode.Default,
|
||||
})
|
||||
);
|
||||
document.title = originTitle.current;
|
||||
dispatch(setBrowserStore({ url: "", title: "", mode: PageMode.Default }));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -171,6 +145,7 @@ const SourceExtract: React.FC<SourceExtractProps> = ({ page = false }) => {
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
url: "",
|
||||
title: "",
|
||||
mode: PageMode.Default,
|
||||
})
|
||||
);
|
||||
@@ -184,20 +159,8 @@ const SourceExtract: React.FC<SourceExtractProps> = ({ page = false }) => {
|
||||
await goto();
|
||||
};
|
||||
|
||||
const onClickLoadItem = async (item: Favorite) => {
|
||||
await loadUrl(item.url);
|
||||
};
|
||||
|
||||
const onDomReady = (e: unknown, data: UrlDetail) => {
|
||||
if (data.url) {
|
||||
document.title = data.title;
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
url: data.url,
|
||||
title: data.title,
|
||||
})
|
||||
);
|
||||
}
|
||||
const onClickLoadItem = (item: Favorite) => {
|
||||
loadUrl(item.url);
|
||||
};
|
||||
|
||||
const onFavoriteEvent = async (
|
||||
@@ -251,17 +214,56 @@ const SourceExtract: React.FC<SourceExtractProps> = ({ page = false }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const prevTitle = document.title;
|
||||
addIpcListener("webview-dom-ready", onDomReady);
|
||||
addIpcListener("webview-fail-load", onFailLoad);
|
||||
addIpcListener("webview-did-navigate", onDidNavigate);
|
||||
addIpcListener("webview-did-navigate-in-page", onDidNavigateInPage);
|
||||
addIpcListener("favorite-item-event", onFavoriteEvent);
|
||||
addIpcListener("show-download-dialog", onShowDownloadDialog);
|
||||
|
||||
return () => {
|
||||
document.title = prevTitle;
|
||||
removeIpcListener("webview-dom-ready", onDomReady);
|
||||
removeIpcListener("webview-fail-load", onFailLoad);
|
||||
removeIpcListener("webview-did-navigate", onDidNavigate);
|
||||
removeIpcListener("webview-did-navigate-in-page", onDidNavigateInPage);
|
||||
removeIpcListener("favorite-item-event", onFavoriteEvent);
|
||||
removeIpcListener("show-download-dialog", onShowDownloadDialog);
|
||||
};
|
||||
}, [store.status]);
|
||||
|
||||
const setPageInfo = ({ url, title }: UrlDetail) => {
|
||||
document.title = title;
|
||||
dispatch(setBrowserStore({ url, title }));
|
||||
};
|
||||
|
||||
const onDomReady = (e: unknown, info: UrlDetail) => {
|
||||
setPageInfo(info);
|
||||
};
|
||||
|
||||
const onFailLoad = (e: unknown, data: { code: number; desc: string }) => {
|
||||
dispatch(
|
||||
setBrowserStore({
|
||||
status: BrowserStatus.Failed,
|
||||
errCode: data.code,
|
||||
errMsg: data.desc,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onDidNavigate = (e: unknown, info: UrlDetail) => {
|
||||
setPageInfo(info);
|
||||
dispatch(setBrowserStore({ status: BrowserStatus.Loaded }));
|
||||
};
|
||||
|
||||
const onDidNavigateInPage = (e: unknown, info: UrlDetail) => {
|
||||
setPageInfo(info);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.title = store.title || document.title;
|
||||
return () => {
|
||||
document.title = originTitle.current;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 合并到主页
|
||||
@@ -385,7 +387,9 @@ const SourceExtract: React.FC<SourceExtractProps> = ({ page = false }) => {
|
||||
);
|
||||
} else if (store.status === BrowserStatus.Failed) {
|
||||
content = (
|
||||
<Empty description={store.errMsg || t("loadFailed")}>
|
||||
<Empty
|
||||
description={`${store.errMsg || t("loadFailed")} (${store.errCode})`}
|
||||
>
|
||||
<Space>
|
||||
<Button type="primary" onClick={onClickGoHome}>
|
||||
{t("backToHome")}
|
||||
@@ -479,35 +483,19 @@ const SourceExtract: React.FC<SourceExtractProps> = ({ page = false }) => {
|
||||
onFavoriteItemContextMenu(item.id);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="list-tem-card"
|
||||
onClick={() => onClickLoadItem(item)}
|
||||
onMouseLeave={() => {
|
||||
setHoverId(-1);
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
setHoverId(item.id);
|
||||
}}
|
||||
>
|
||||
{hoverId === item.id && (
|
||||
<Button
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: -1,
|
||||
top: 2,
|
||||
color: "gray",
|
||||
}}
|
||||
type="link"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
removeFavorite(item.id);
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
)}
|
||||
<div className="list-tem-card" onClick={() => onClickLoadItem(item)}>
|
||||
<Button
|
||||
className="favorite-remove-btn"
|
||||
type="link"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
removeFavorite(item.id);
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
{item.icon ? (
|
||||
<Avatar shape="square" src={item.icon} icon={<LinkOutlined />} />
|
||||
) : (
|
||||
|
||||
Vendored
+9
@@ -72,6 +72,14 @@ declare interface AppStore {
|
||||
language?: AppLanguage;
|
||||
// 是否显示终端
|
||||
showTerminal?: boolean;
|
||||
// 隐私模式
|
||||
privacy?: boolean;
|
||||
// 机器id
|
||||
machineId?: string;
|
||||
// 下载代理设置
|
||||
downloadProxySwitch?: boolean;
|
||||
// 自动更新
|
||||
autoUpgrade?: boolean;
|
||||
}
|
||||
|
||||
declare interface BrowserStore {
|
||||
@@ -79,6 +87,7 @@ declare interface BrowserStore {
|
||||
url: string;
|
||||
title: string;
|
||||
status: BrowserStatus;
|
||||
errCode?: number;
|
||||
errMsg?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ const initialState: AppStore = {
|
||||
maxRunner: 2,
|
||||
language: AppLanguage.System,
|
||||
showTerminal: false,
|
||||
privacy: false,
|
||||
machineId: "",
|
||||
downloadProxySwitch: false,
|
||||
autoUpgrade: true,
|
||||
};
|
||||
|
||||
export const appSlice = createSlice({
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// prettier-ignore
|
||||
export function initClarity() {
|
||||
try {
|
||||
(function(c,l,a,r,i,t,y){
|
||||
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
||||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||
})(window, document, "clarity", "script", import.meta.env.APP_CLARITY_APPID);
|
||||
} catch (e) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { isUrl } from "./url";
|
||||
|
||||
export { http } from "./http";
|
||||
export { tdApp } from "./tdapp";
|
||||
export { initClarity } from "./clarity";
|
||||
|
||||
export const requestImage = (url: string, timeout = 1000): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -42,7 +41,7 @@ export const generateUrl = (url: string) => {
|
||||
result = `http://${url}`;
|
||||
}
|
||||
if (!isUrl(result)) {
|
||||
result = `https://baidu.com/s?word=${url}`;
|
||||
result = `https://www.baidu.com/s?word=${url}`;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ class TDEvent {
|
||||
headElement.appendChild(script);
|
||||
}
|
||||
|
||||
private onEvent(eventId: string, mapKv: Record<string, string> = {}) {
|
||||
onEvent(eventId: string, mapKv: Record<string, string> = {}) {
|
||||
try {
|
||||
window.TDAPP?.onEvent(eventId, "", mapKv);
|
||||
} catch (e) {
|
||||
|
||||
Vendored
+1
-2
@@ -8,10 +8,9 @@ declare global {
|
||||
onEvent: (
|
||||
eventId: string,
|
||||
label: "",
|
||||
mapKv: Record<string, string>,
|
||||
mapKv: Record<string, string>
|
||||
) => void;
|
||||
};
|
||||
clarity?: any;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+20
-9
@@ -74,10 +74,10 @@ importers:
|
||||
packages/main:
|
||||
dependencies:
|
||||
'@cliqz/adblocker-electron':
|
||||
specifier: ^1.27.3
|
||||
specifier: 1.27.3
|
||||
version: 1.27.3(electron@30.0.8)
|
||||
'@cliqz/adblocker-electron-preload':
|
||||
specifier: ^1.27.3
|
||||
specifier: 1.27.3
|
||||
version: 1.27.3(electron@30.0.8)
|
||||
axios:
|
||||
specifier: ^1.7.2
|
||||
@@ -121,9 +121,15 @@ importers:
|
||||
mime-types:
|
||||
specifier: ^2.1.35
|
||||
version: 2.1.35
|
||||
nanoid:
|
||||
specifier: ^5.0.7
|
||||
version: 5.0.7
|
||||
node-fetch:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
node-machine-id:
|
||||
specifier: ^1.1.12
|
||||
version: 1.1.12
|
||||
node-pty:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
@@ -4206,6 +4212,9 @@ packages:
|
||||
engines: {node: ^12.13 || ^14.13 || >=16}
|
||||
hasBin: true
|
||||
|
||||
node-machine-id@1.1.12:
|
||||
resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==}
|
||||
|
||||
node-pty@1.0.0:
|
||||
resolution: {integrity: sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==}
|
||||
|
||||
@@ -7813,7 +7822,7 @@ snapshots:
|
||||
|
||||
app-builder-bin@4.0.0: {}
|
||||
|
||||
app-builder-lib@24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)):
|
||||
app-builder-lib@24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)):
|
||||
dependencies:
|
||||
'@develar/schema-utils': 2.6.5
|
||||
'@electron/notarize': 2.2.1
|
||||
@@ -7827,7 +7836,7 @@ snapshots:
|
||||
builder-util-runtime: 9.2.4
|
||||
chromium-pickle-js: 0.2.0
|
||||
debug: 4.3.4
|
||||
dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3)
|
||||
ejs: 3.1.10
|
||||
electron-builder-squirrel-windows: 24.13.3(dmg-builder@24.13.3)
|
||||
electron-publish: 24.13.1
|
||||
@@ -8619,9 +8628,9 @@ snapshots:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
||||
dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)):
|
||||
dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3):
|
||||
dependencies:
|
||||
app-builder-lib: 24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
app-builder-lib: 24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
builder-util: 24.13.1
|
||||
builder-util-runtime: 9.2.4
|
||||
fs-extra: 10.1.0
|
||||
@@ -8694,7 +8703,7 @@ snapshots:
|
||||
|
||||
electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3):
|
||||
dependencies:
|
||||
app-builder-lib: 24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
app-builder-lib: 24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
archiver: 5.3.2
|
||||
builder-util: 24.13.1
|
||||
fs-extra: 10.1.0
|
||||
@@ -8704,11 +8713,11 @@ snapshots:
|
||||
|
||||
electron-builder@24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)):
|
||||
dependencies:
|
||||
app-builder-lib: 24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3)))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
app-builder-lib: 24.13.3(dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3))(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
builder-util: 24.13.1
|
||||
builder-util-runtime: 9.2.4
|
||||
chalk: 4.1.2
|
||||
dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||
dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3)
|
||||
fs-extra: 10.1.0
|
||||
is-ci: 3.0.1
|
||||
lazy-val: 1.0.5
|
||||
@@ -10137,6 +10146,8 @@ snapshots:
|
||||
- bluebird
|
||||
- supports-color
|
||||
|
||||
node-machine-id@1.1.12: {}
|
||||
|
||||
node-pty@1.0.0:
|
||||
dependencies:
|
||||
nan: 2.19.0
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { ProcessOutput } from "zx/core";
|
||||
|
||||
export function printObject(
|
||||
object: Record<string, unknown> | ProcessOutput,
|
||||
method: "log" | "warn" | "error" = "log",
|
||||
method: "log" | "warn" | "error" = "log"
|
||||
) {
|
||||
for (const [key, value] of Object.entries(object)) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
Reference in New Issue
Block a user