304 lines
9.1 KiB
TypeScript
304 lines
9.1 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import fs from "node:fs/promises";
|
|
import { createRequire } from "node:module";
|
|
import os from "node:os";
|
|
import path, { dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { promisify } from "node:util";
|
|
import dotenvFlow from "dotenv-flow";
|
|
import { type Configuration, build } from "electron-builder";
|
|
|
|
const args = process.argv.slice(2);
|
|
const isDir = args.includes("--dir");
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
const projectRoot = path.resolve(__dirname, "../../..");
|
|
const appRoot = path.resolve(__dirname, "..");
|
|
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
|
|
// Resolve electron-builder's bundled `app-builder` native helper
|
|
// (wraps rcedit, ships inside `app-builder-bin`). We use it from
|
|
// `afterAllArtifactBuild` below to rewrite the NSIS installer's
|
|
// FileDescription — see the hook for why. `app-builder-bin` is
|
|
// a transitive dep of electron-builder, not a direct one, so we
|
|
// resolve it via nested `createRequire` scoped to electron-builder's
|
|
// own location.
|
|
const execFileAsync = promisify(execFile);
|
|
const localRequire = createRequire(import.meta.url);
|
|
const electronBuilderRequire = createRequire(
|
|
localRequire.resolve("electron-builder/package.json"),
|
|
);
|
|
const { appBuilderPath } = electronBuilderRequire("app-builder-bin") as {
|
|
appBuilderPath: string;
|
|
};
|
|
|
|
dotenvFlow.config({
|
|
path: projectRoot,
|
|
});
|
|
|
|
const pkg = JSON.parse(await fs.readFile("./app/package.json", "utf-8"));
|
|
|
|
function getReleaseConfig(): Configuration {
|
|
return {
|
|
asar: true,
|
|
asarUnpack: [],
|
|
productName: process.env.APP_NAME,
|
|
buildVersion: pkg.version,
|
|
appId: process.env.APP_ID,
|
|
copyright: process.env.APP_COPYRIGHT,
|
|
// Register the custom scheme at install/first-run time.
|
|
// • macOS: electron-builder merges this into CFBundleURLTypes.
|
|
// • Windows: runtime `app.setAsDefaultProtocolClient` in src/index.ts
|
|
// handles registry entries on first launch — electron-builder
|
|
// doesn't write these directly.
|
|
// • Linux: runtime handler writes the .desktop file at first launch.
|
|
protocols: [
|
|
{
|
|
name: "MediaGo URL Scheme",
|
|
schemes: [process.env.APP_NAME as string],
|
|
},
|
|
],
|
|
artifactName:
|
|
"${productName}-setup-${platform}-${arch}-${buildVersion}.${ext}",
|
|
npmRebuild: true,
|
|
directories: {
|
|
output: "./release",
|
|
},
|
|
files: [
|
|
{
|
|
from: "./build/main",
|
|
to: "./main",
|
|
},
|
|
{
|
|
from: "./build/renderer",
|
|
to: "./renderer",
|
|
},
|
|
{
|
|
from: "./build/preload",
|
|
to: "./preload",
|
|
},
|
|
"./package.json",
|
|
"!**/node_modules/**",
|
|
"**/node_modules/@mediago/electron-preload/**",
|
|
"**/node_modules/@mediago/browser-extension/**",
|
|
"**/node_modules/@ghostery/adblocker-electron-preload/**",
|
|
],
|
|
extraResources: [
|
|
{
|
|
from: "./app/build/plugin",
|
|
to: "plugin",
|
|
},
|
|
{
|
|
from: "./app/build/bin",
|
|
to: "bin",
|
|
},
|
|
{
|
|
from: "./app/build/deps",
|
|
to: "deps",
|
|
},
|
|
{
|
|
// MediaGo browser extension (Manifest V3). Shipped unpacked
|
|
// inside the installer's `resources/extension/` so users can
|
|
// "Load unpacked" it from Chrome / Edge via the Settings page's
|
|
// "Browser extension directory" button. The runtime path is
|
|
// resolved by `resolveExtensionDir()` in binaryResolver.ts.
|
|
from: "./app/build/extension",
|
|
to: "extension",
|
|
},
|
|
],
|
|
win: {
|
|
icon: "../assets/icon.ico",
|
|
target: [
|
|
{
|
|
target: "nsis",
|
|
arch,
|
|
},
|
|
{
|
|
target: "portable",
|
|
arch,
|
|
},
|
|
],
|
|
},
|
|
portable: {
|
|
artifactName:
|
|
"${productName}-portable-${platform}-${arch}-${buildVersion}.${ext}",
|
|
},
|
|
dmg: {
|
|
contents: [
|
|
{
|
|
x: 410,
|
|
y: 150,
|
|
type: "link",
|
|
path: "/Applications",
|
|
},
|
|
{
|
|
x: 130,
|
|
y: 150,
|
|
type: "file",
|
|
},
|
|
],
|
|
},
|
|
mac: {
|
|
icon: "../assets/icon.icns",
|
|
identity: null,
|
|
target: [
|
|
{
|
|
target: "dmg",
|
|
arch,
|
|
},
|
|
],
|
|
extendInfo: {
|
|
CFBundleURLTypes: [
|
|
{
|
|
CFBundleURLName: "MediaGo URL Scheme",
|
|
// Must match `process.env.APP_NAME` in .env — the same
|
|
// scheme used by src/index.ts's setAsDefaultProtocolClient
|
|
// and by the browser extension's MEDIAGO_SCHEME.
|
|
CFBundleURLSchemes: [process.env.APP_NAME as string],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
linux: {
|
|
category: "Utility",
|
|
icon: "../assets/icon.icns",
|
|
maintainer: "caorushizi <84996057@qq.com>",
|
|
target: {
|
|
target: "deb",
|
|
arch,
|
|
},
|
|
},
|
|
nsis: {
|
|
oneClick: false,
|
|
allowElevation: true,
|
|
allowToChangeInstallationDirectory: true,
|
|
createDesktopShortcut: true,
|
|
createStartMenuShortcut: true,
|
|
// Inject our customHeader macro to add the version to the
|
|
// installer title bar. See comments in
|
|
// `apps/electron/installer/installer.nsh`.
|
|
include: "./installer/installer.nsh",
|
|
},
|
|
// Rewrite the NSIS installer's `FileDescription` after the fact.
|
|
//
|
|
// Why this isn't done in the .nsh header: electron-builder's
|
|
// `NsisTarget.computeVersionKey()` unconditionally emits
|
|
// VIAddVersionKey /LANG=1033 "FileDescription" "${appInfo.description}"
|
|
// into the generated .nsi — binding the installer's FileDescription
|
|
// to the app binary's (both drawn from `app/package.json:description`).
|
|
// Any customHeader `VIAddVersionKey` with the same LANG+key triggers
|
|
// a hard NSIS "already defined!" error that `-WX` does not gate, and
|
|
// a different LANG (e.g. 0) triggers `warning 9100: without standard
|
|
// key FileVersion` which IS gated by `-WX`. There is no in-NSIS way
|
|
// to override this cleanly.
|
|
//
|
|
// Inno Setup (used by VS Code, Chrome) gets this for free via a
|
|
// default `VersionInfoDescription = "{AppName} Setup"`. NSIS has no
|
|
// such default, so we post-process the artifact with the same
|
|
// `app-builder rcedit` call electron-builder itself uses on
|
|
// `mediago.exe` (see winPackager.js around line 185).
|
|
afterAllArtifactBuild: async ({ artifactPaths }) => {
|
|
// rcedit crashes when executed through Wine (per electron-builder's
|
|
// own note in winPackager.js:183); skip on Linux. Windows installer
|
|
// artifacts aren't produced on Linux builds anyway.
|
|
if (process.platform !== "win32" && process.platform !== "darwin") {
|
|
return [];
|
|
}
|
|
const installers = artifactPaths.filter((p) =>
|
|
/-setup-win32-.*\.exe$/i.test(path.basename(p)),
|
|
);
|
|
for (const installer of installers) {
|
|
await execFileAsync(appBuilderPath, [
|
|
"rcedit",
|
|
"--args",
|
|
JSON.stringify([
|
|
installer,
|
|
"--set-version-string",
|
|
"FileDescription",
|
|
`${process.env.APP_NAME} installer`,
|
|
]),
|
|
]);
|
|
}
|
|
return [];
|
|
},
|
|
};
|
|
}
|
|
|
|
const electronTask = [
|
|
{
|
|
src: "apps/electron/build",
|
|
dest: "app/build/main",
|
|
},
|
|
{
|
|
src: "apps/ui/build/electron",
|
|
dest: "app/build/renderer",
|
|
},
|
|
{
|
|
src: "packages/electron-preload/build",
|
|
dest: "app/build/preload",
|
|
},
|
|
{
|
|
src: "packages/browser-extension/build",
|
|
dest: "app/build/plugin",
|
|
},
|
|
{
|
|
// MediaGo browser extension dist — produced upstream by
|
|
// `turbo run build -F @mediago/extension` (wired into
|
|
// `pnpm build:electron` at the repo root). Gets zipped into the
|
|
// installer via the `extraResources` entry above.
|
|
src: "packages/mediago-extension/dist",
|
|
dest: "app/build/extension",
|
|
},
|
|
];
|
|
|
|
for (const task of electronTask) {
|
|
await fs.cp(
|
|
path.resolve(projectRoot, task.src),
|
|
path.resolve(appRoot, task.dest),
|
|
{
|
|
recursive: true,
|
|
force: true,
|
|
},
|
|
);
|
|
}
|
|
|
|
// Copy Go binaries to app/build/bin/ and app/build/deps/ for extraResources
|
|
const isWindows = os.platform() === "win32";
|
|
const ext = isWindows ? ".exe" : "";
|
|
const binDir = path.resolve(appRoot, "app/build/bin");
|
|
const depsDir = path.resolve(appRoot, "app/build/deps");
|
|
|
|
await fs.mkdir(binDir, { recursive: true });
|
|
await fs.mkdir(depsDir, { recursive: true });
|
|
|
|
// Copy mediago-core binary + config
|
|
await fs.copyFile(
|
|
path.resolve(projectRoot, `apps/core/bin/mediago-core${ext}`),
|
|
path.join(binDir, `mediago-core${ext}`),
|
|
);
|
|
|
|
// Copy dependency binaries (ffmpeg, N_m3u8DL-RE, BBDown, aria2c, yt-dlp)
|
|
const platformKey = `${os.platform()}-${os.arch()}`;
|
|
const localDepsDir = path.resolve(projectRoot, ".deps", platformKey);
|
|
try {
|
|
await fs.cp(localDepsDir, depsDir, { recursive: true, force: true });
|
|
} catch {
|
|
console.warn(`Warning: .deps/${platformKey} not found, skipping deps copy`);
|
|
}
|
|
|
|
const config = getReleaseConfig();
|
|
if (process.env.GH_TOKEN) {
|
|
config.publish = {
|
|
provider: "github",
|
|
repo: "mediago",
|
|
owner: "caorushizi",
|
|
releaseType: "draft",
|
|
};
|
|
}
|
|
await build({
|
|
config,
|
|
dir: isDir,
|
|
// targets: Platform.WINDOWS.createTarget(["nsis", "portable"], Arch.x64),
|
|
});
|