diff --git a/app/package.json b/app/package.json index 3d31105..12043c1 100755 --- a/app/package.json +++ b/app/package.json @@ -1,4 +1,5 @@ { + "type": "module", "dependencies": { "simple-node-logger": "^21.8.12", "base64-js": "^1.5.1", diff --git a/app/src/config.js b/app/src/config.js new file mode 100644 index 0000000..6851852 --- /dev/null +++ b/app/src/config.js @@ -0,0 +1,9 @@ +// experimental +//import config from './config.json' with { type: "json" }; + +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); +import fs from 'fs'; +const config = JSON.parse(fs.readFileSync(require.resolve('./config.json'), 'utf8')); + +export default config; diff --git a/app/src/converter.js b/app/src/converter.js index af7b4c3..15ea0e1 100644 --- a/app/src/converter.js +++ b/app/src/converter.js @@ -1,11 +1,9 @@ import open from 'open'; +import os from "os"; +import path from "path"; +import fs from "node:fs"; -const os = require("os"); -const path = require('path'); -const fs = require("node:fs"); - -const logger = require('./logger'); -const rpc = require('./weh-rpc'); +export default ({logger, rpc}) => { const exec_dir = path.dirname(process.execPath); @@ -70,7 +68,10 @@ function spawn(arg0, argv) { } for (let e of ["exit", "SIGINT", "SIGTERM", "uncaughtException"]) { - process.on(e, () => { + process.on(e, (...args) => { + if (e == "uncaughtException") { + console.error(args[0]); + } for (let process of to_kill) { try { process.kill(9); @@ -99,7 +100,7 @@ function ExecConverter(args) { }); } -exports.star_listening = () => { +const start_listening = () => { const convertChildren = new Map(); @@ -334,7 +335,7 @@ exports.star_listening = () => { }); }; -exports.info = () => { +const info = () => { return new Promise((resolve, reject) => { let convProcess = spawn(ffmpeg, ["-h"]); let done = false; @@ -365,3 +366,10 @@ exports.info = () => { }); }); }; + +return { + start_listening, + info, +}; + +} diff --git a/app/src/downloads.js b/app/src/downloads.js index 2ca6368..e0ba451 100644 --- a/app/src/downloads.js +++ b/app/src/downloads.js @@ -1,8 +1,7 @@ -const path = require('path'); -const fs = require('node:fs'); -const rpc = require('./weh-rpc'); -const os = require("os"); -const got = require('got'); +import path from "path"; +import fs from "node:fs"; +import os from "os"; +import got from "got"; let downloadFolder = path.join(os.homedir(), "dwhelper"); @@ -156,7 +155,7 @@ function cancel(downloadId) { } } -rpc.listen({ +export default ({rpc}) => rpc.listen({ "downloads.download": download, "downloads.search": search, "downloads.cancel": cancel diff --git a/app/src/file.js b/app/src/file.js index 8e0cf74..d46aff7 100644 --- a/app/src/file.js +++ b/app/src/file.js @@ -1,15 +1,14 @@ -const fs = require('node:fs'); -const path = require('path'); -const tmp = require('tmp'); -const rpc = require('./weh-rpc'); -const { spawn } = require('child_process'); -const os = require('os'); -const base64 = require("base64-js"); +import fs from 'node:fs'; +import path from 'path'; +import tmp from 'tmp'; +import { spawn } from 'child_process'; +import os from 'os'; +import base64 from "base64-js"; const uniqueFileNames = {}; const MAX_FILE_ENTRIES = 1000; -rpc.listen({ +export default ({rpc}) => rpc.listen({ // In test suite "listFiles": (directory) => { return new Promise((resolve, reject) => { diff --git a/app/src/logger.js b/app/src/logger.js index 56b2327..07285e0 100644 --- a/app/src/logger.js +++ b/app/src/logger.js @@ -1,15 +1,15 @@ -let simplelogger = require("simple-node-logger"); +import simplelogger from "simple-node-logger"; -let logfile = process.env.WEH_NATIVE_LOGFILE; +const logfile = process.env.WEH_NATIVE_LOGFILE; +//const logfile = "/run/user/1000/vdhcoapp.log"; // debug -if (!logfile) { - module.exports = { +export default () => (!logfile) ? ( + { info: () => {}, error: () => {}, warn: () => {}, log: () => {}, - }; -} else { - let logger = simplelogger.createSimpleFileLogger(logfile); - module.exports = logger; -} + } +) : ( + simplelogger.createSimpleFileLogger(logfile) +); diff --git a/app/src/main.js b/app/src/main.js index 47b92de..c10e6b7 100644 --- a/app/src/main.js +++ b/app/src/main.js @@ -1,6 +1,13 @@ -const config = require('config.json'); -const converter = require('./converter'); -const os = require("os"); +import config from './config.js'; + +import os from 'os'; + +const globalState = { + config, + logger: null, + rpc: null, + converter: null, +}; function info() { let result = { @@ -13,7 +20,7 @@ function info() { target: config.target, home: os.homedir() || "" }; - return converter.info().then((convInfo) => { + return globalState.converter.info().then((convInfo) => { return Object.assign(result, { converterBinary: convInfo.converterBinary, converterBase: convInfo.program, @@ -27,9 +34,9 @@ function info() { } if (process.argv[2] == "install") { - require("./native-autoinstall").install(); + (await import("./native-autoinstall.js")).install(); } else if (process.argv[2] == "uninstall") { - require("./native-autoinstall").uninstall(); + (await import("./native-autoinstall.js")).uninstall(); } else if (process.argv[2] == "--version") { console.log(config.meta.version); } else if (process.argv[2] == "--info") { @@ -54,19 +61,27 @@ Options: console.log(help); } else { - require('./native-messaging'); - const logger = require('./logger'); - const rpc = require('./weh-rpc'); + // note: this disables the exception handler + // so exceptions will silently fail + // fix: WEH_NATIVE_LOGFILE=/dev/stderr vdhcoapp + + const logger = (await import('./logger.js')).default(); + globalState.logger = logger; + const rpc = (await import('./weh-rpc.js')).default(); rpc.setLogger(logger); rpc.setDebugLevel(2); + globalState.rpc = rpc; - require('./file'); - require('./downloads'); - require('./request'); - require('./vm'); + const converter = (await import('./converter.js')).default(globalState); + globalState.converter = converter; - converter.star_listening(); + (await import('./file.js')).default(globalState); + (await import('./downloads.js')).default(globalState); + (await import('./request.js')).default(globalState); + (await import('./vm.js')).default(globalState); + + converter.start_listening(); rpc.listen({ // In test suite @@ -85,6 +100,9 @@ Options: info, }); + // start listening for RPC requests from stdin + (await import('./native-messaging.js')).default(globalState); + let m = `vdhcoapp is running successfully. `; m += `This is not intended to be used directly from the command line. `; m += `You should press Ctrl+C to exit. `; diff --git a/app/src/native-autoinstall.js b/app/src/native-autoinstall.js index 556a22b..ae808ce 100644 --- a/app/src/native-autoinstall.js +++ b/app/src/native-autoinstall.js @@ -1,13 +1,16 @@ -const os = require("os"); -const path = require("path"); -const { spawn, exec } = require('child_process'); -const config = require('config.json'); +import os from "os"; +import path from "path"; +import { spawn, exec } from "child_process"; +import config from "./config.js"; + +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); let fs; if (process.versions.node.startsWith("10")) { - fs = require('fs').promises; + fs = await require('fs').promises; } else { - fs = require('node:fs/promises'); + fs = await require('node:fs/promises'); } const STORES = Object.keys(config.store); @@ -155,6 +158,10 @@ async function PrepareFlatpak() { } async function install_uninstall(uninstall = false) { + if (process.env.VDHCOAPP_INSTALL_ON_BUILDTIME != "1") { + // VDHCOAPP_INSTALL_ON_BUILDTIME=1 vdhcoapp install + return await install_uninstall_on_runtime(uninstall); + } let platform = os.platform(); if (platform == "darwin") { let mode = GetMode(); @@ -170,12 +177,77 @@ async function install_uninstall(uninstall = false) { } } -exports.install = () => { +export const install = async () => { console.log("Installing…"); - install_uninstall(); + await install_uninstall(); }; -exports.uninstall = () => { +export const uninstall = async () => { console.log("Uninstalling…"); - install_uninstall(true); + await install_uninstall(true); }; + +async function fs_exists(path) { + try { + await fs.access(path, fs.constants.F_OK); + return true; + } + catch (e) { + if (e.errno == -2) { + return false; + } + throw e; + } +} + +async function install_uninstall_on_runtime(uninstall) { + const glob = (await import('glob')).default; + const srcMainJsPath = await fs.realpath(process.argv[1]); + const srcConfigDirPath = path.join(path.dirname(srcMainJsPath), "../config", os.platform()); + if (!await fs_exists(srcConfigDirPath)) { + console.error(`error: not found config dir: ${srcConfigDirPath}`); + process.exit(1); + } + const pattern = "**/*.json"; + const options = { + cwd: srcConfigDirPath, + nodir: true, // Do not match directories, only files + follow: true, // Follow symlinked directories when expanding ** patterns + dot: true, // match hidden paths + //realpath: true, // call fs.realpath on all of the results + }; + const homedir = os.homedir(); + for (const configPath of glob.sync(pattern, options)) { + const dstConfigPath = path.join(homedir, configPath); + const dstConfigDirPath = path.join(homedir, path.dirname(configPath), ".."); + if (!await fs_exists(dstConfigDirPath)) { + console.log(`missing ${dstConfigDirPath}`); + continue; + } + const srcConfigPath = await fs.realpath(path.join(srcConfigDirPath, configPath)); + if (uninstall) { + if (await fs_exists(dstConfigPath)) { + console.log(`deleting ${dstConfigPath}`); + await fs.unlink(dstConfigPath); + } + continue; + } + if (!await fs_exists(dstConfigPath)) { + console.log(`creating ${dstConfigPath}`); + await fs.mkdir(path.dirname(dstConfigPath), { recursive: true }); + await fs.symlink(srcConfigPath, dstConfigPath); + continue; + } + // check if file is up to date + const oldLinkTarget = await fs.realpath(dstConfigPath); + if (oldLinkTarget == srcConfigPath) { + // file is up to date + console.log(`keeping ${dstConfigPath}`); + continue; + } + // found different file. delete the old file + console.log(`replacing ${dstConfigPath}`); + await fs.unlink(dstConfigPath); + await fs.symlink(srcConfigPath, dstConfigPath); + } +} diff --git a/app/src/native-messaging.js b/app/src/native-messaging.js index fb082cd..4b2c0e0 100644 --- a/app/src/native-messaging.js +++ b/app/src/native-messaging.js @@ -1,5 +1,4 @@ -const rpc = require('./weh-rpc'); -const logger = require('./logger'); +export default ({logger, rpc}) => { logger.info("\n\n\n\n=================== started ===================="); @@ -56,3 +55,5 @@ rpc.setPost(Send); process.stdin.on('data', (chunk) => { AppendInputString(chunk); }); + +} diff --git a/app/src/request.js b/app/src/request.js index e8282e5..f329007 100644 --- a/app/src/request.js +++ b/app/src/request.js @@ -1,6 +1,4 @@ -const rpc = require('./weh-rpc'); -const got = require('got'); -const logger = require('./logger'); +import got from 'got'; let currentIndex = 0; @@ -9,6 +7,8 @@ let requestStore = {}; const MAX_SIZE = 50000; const EXPIRE_DATA_TIMEOUT = 30000; +export default ({logger, rpc}) => { + function GetData(id) { let reqInfo = requestStore[id]; if (!reqInfo) { @@ -184,3 +184,5 @@ rpc.listen({ return GetData(id); } }); + +} diff --git a/app/src/vm.js b/app/src/vm.js index d6e23bf..c7d4a77 100644 --- a/app/src/vm.js +++ b/app/src/vm.js @@ -1,7 +1,6 @@ -const vm = require('vm'); -const rpc = require('./weh-rpc'); +import vm from 'vm'; -rpc.listen({ +export default ({rpc}) => rpc.listen({ "vm.run": async (code) => { const sandbox = {}; const script = new vm.Script(code); diff --git a/app/src/weh-rpc.js b/app/src/weh-rpc.js index 10efcec..bf46d29 100644 --- a/app/src/weh-rpc.js +++ b/app/src/weh-rpc.js @@ -194,4 +194,4 @@ class RPC { } -module.exports = new RPC(); +export default () => new RPC();