node integration bgutils

pull/1327/head
deniscerri 3 weeks ago
parent 8b464c9d0c
commit 1024ecef84
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -590,6 +590,7 @@ class MainActivity : BaseActivity() {
val latestRelease = releases.first()
if (latestRelease.isBundled || latestRelease.isInstalled) return@apply
if (latestRelease.oldVersion) return@apply
if (skipRemindingPackageUpdate.contains(latestRelease.tag_name)) return@apply
skipRemindingPackageUpdate.add(latestRelease.tag_name)

@ -67,6 +67,11 @@ object RuntimeManager {
private var ENV_PYTHONHOME: String? = null
private var TMPDIR: String = ""
private var NPM_CONFIG_PREFIX: String = ""
private var NPM_CONFIG_CACHE: String = ""
private var NPM_CLI_PATH: String = ""
private var NODE_OPTIONS: String = ""
val packages: List<PackageItem> = listOf(
PackageItem("Python", Python),
PackageItem("FFmpeg", FFmpeg),
@ -154,6 +159,16 @@ object RuntimeManager {
}
TMPDIR = appContext.cacheDir.absolutePath
NPM_CONFIG_PREFIX = File(appContext.filesDir, ".npm-global").absolutePath
NPM_CONFIG_CACHE = File(appContext.filesDir, ".npm-cache").absolutePath
if (nodeLocation.executable.exists()) {
NPM_CLI_PATH = File(nodeLocation.ldDir.absolutePath, "usr/lib/node_modules/npm/bin/npm-cli.js").absolutePath
val optionsFile = File(appContext.filesDir, "node_dns_setup.js")
optionsFile.writeText(NodeJS.getDNSSetup())
NODE_OPTIONS = "--require ${optionsFile.absolutePath}"
}
initialized = true
initLatch.countDown()
updateLatch.countDown()
@ -292,6 +307,33 @@ object RuntimeManager {
return executeImpl(fullCommand, processId, true, callback = callback)
}
fun executeNode(
command: String,
processId: String? = null,
executeDirectory: File? = null,
callback: ((Float, Long, String) -> Unit)? = null
) : ExecuteResponse {
assertInit()
val fullCommand = mutableListOf<String>(nodeLocation.executable.absolutePath)
fullCommand.addAll(command.split(" "))
return executeImpl(fullCommand, processId, true, executeDirectory = executeDirectory, callback = callback)
}
fun executeNpm(
command: String,
processId: String? = null,
executeDirectory: File? = null,
callback: ((Float, Long, String) -> Unit)? = null
) : ExecuteResponse {
assertInit()
val fullCommand = mutableListOf<String>(nodeLocation.executable.absolutePath, NPM_CLI_PATH)
fullCommand.addAll(command.split(" "))
return executeImpl(fullCommand, processId, true, executeDirectory = executeDirectory, callback = callback)
}
fun executeDeno(
command: String,
processId: String? = null,
@ -447,6 +489,10 @@ object RuntimeManager {
env["PYTHONHOME"] = ENV_PYTHONHOME
env["HOME"] = ENV_PYTHONHOME
env["TMPDIR"] = TMPDIR
env["NPM_CONFIG_PREFIX"] = NPM_CONFIG_PREFIX
env["NPM_CONFIG_CACHE"] = NPM_CONFIG_CACHE
env["NPM_CLI_PATH"] = NPM_CLI_PATH
env["NODE_OPTIONS"] = NODE_OPTIONS
env["TERM"] = "xterm-256color"
return env

@ -9,4 +9,76 @@ object NodeJS : PackageBase() {
override val githubRepo: String get() = "deniscerri/ytdlnis-packages"
override val githubPackageName: String get() = "nodejs"
override val apkPackage: String get() = "com.deniscerri.ytdl.nodejs"
fun getDNSSetup() : String {
return """
const dns = require('dns');
const net = require('net');
const SERVERS = ['8.8.8.8', '1.1.1.1'];
dns.setServers(SERVERS);
const resolver = new dns.Resolver();
resolver.setServers(SERVERS);
dns.lookup = function patchedLookup(hostname, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
options = options || {};
const wantAll = options.all === true;
const family = options.family || 0; // 0 = either
// Literal IPs / wildcard bind addresses: resolve instantly, no network.
const ipVersion = net.isIP(hostname);
if (ipVersion) {
if (wantAll) return callback(null, [{ address: hostname, family: ipVersion }]);
return callback(null, hostname, ipVersion);
}
// localhost: resolve instantly, no network.
if (hostname === 'localhost') {
const results = [];
if (family !== 6) results.push({ address: '127.0.0.1', family: 4 });
if (family !== 4) results.push({ address: '::1', family: 6 });
if (wantAll) return callback(null, results);
const first = results[0];
return callback(null, first.address, first.family);
}
// Real hostnames: resolve via c-ares against our explicit DNS servers.
const tryFamily = (fam, cb) => {
const method = fam === 6 ? 'resolve6' : 'resolve4';
resolver[method](hostname, (err, addresses) => {
if (err) return cb(err);
cb(null, addresses.map((address) => ({ address, family: fam })));
});
};
const finish = (err, results) => {
if (err) return callback(err);
if (wantAll) return callback(null, results);
const first = results[0];
callback(null, first.address, first.family);
};
if (family === 4 || family === 6) {
return tryFamily(family, (err, results) => finish(err, results));
}
// Try IPv4 first, fall back to IPv6 if that fails.
tryFamily(4, (err4, results4) => {
if (!err4) return finish(null, results4);
tryFamily(6, (err6, results6) => {
if (!err6) return finish(null, results6);
finish(err4); // report the original (v4) error
});
});
};
""".trimIndent()
}
}

@ -51,7 +51,8 @@ abstract class PackageBase {
var version: String = "",
var downloadSize: Long = 0,
var isInstalled: Boolean = false,
var isBundled: Boolean = false
var isBundled: Boolean = false,
var oldVersion: Boolean = false
)
data class PackageLocation(
@ -285,6 +286,11 @@ abstract class PackageBase {
it.downloadSize = it.assets.first().size
it.isInstalled = downloadedVersion == "v${it.version}"
it.isBundled = bundledVersion == "v${it.version}"
val latestVersion = bundledVersion?.replace("v|.".toRegex(), "")?.toInt()
?: downloadedVersion?.replace("v|.".toRegex(), "")?.toInt() ?: 0
it.oldVersion = latestVersion > it.version.replace(".", "").toInt()
}
Result.success(releases)

@ -60,8 +60,10 @@ class BgUtilsPoTokenGeneratorService : Service() {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val runtimeManager = RuntimeManager.getInstance()
if (intent?.action == "ACTION_EXIT") {
RuntimeManager.getInstance().destroyProcessById(currentRunningProcess)
runtimeManager.destroyProcessById(currentRunningProcess)
stopSelf()
return super.onStartCommand(intent, flags, startId)
}
@ -69,16 +71,32 @@ class BgUtilsPoTokenGeneratorService : Service() {
serviceScope.launch {
runCatching {
val serverFolder = BgUtilsPoTokenGeneratorUtil.getServerFolder(App.instance)
RuntimeManager.getInstance().destroyProcessById(currentRunningProcess)
RuntimeManager.getInstance().executeDeno(
command = "run -A src/main.ts",
processId = currentRunningProcess,
executeDirectory = File(serverFolder, "server")
) { _, _, line ->
Log.e("BGUTILS_POT", line)
val notification = createNotification(line)
notificationManager.notify(notificationCode, notification)
runtimeManager.destroyProcessById(currentRunningProcess)
if (runtimeManager.nodeLocation.isAvailable) {
runtimeManager.executeNode(
command = "build/main.js",
processId = currentRunningProcess,
executeDirectory = File(serverFolder, "server")
) { _, _, line ->
Log.e("BGUTILS_POT", line)
val notification = createNotification(line)
notificationManager.notify(notificationCode, notification)
}
} else {
runtimeManager.executeDeno(
command = "run -A src/main.ts",
processId = currentRunningProcess,
executeDirectory = File(serverFolder, "server")
) { _, _, line ->
Log.e("BGUTILS_POT", line)
val notification = createNotification(line)
notificationManager.notify(notificationCode, notification)
}
}
}.onFailure { err ->
Log.e("BGUTILS_POT", err.message ?: "")
stopSelf()

@ -166,23 +166,14 @@ class GenerateYoutubePoTokensFragment : Fragment() {
}
serverRadio.apply {
isChecked = bgUtilsMethod == "server" && denoIsInstalled
alpha = if (denoIsInstalled) 1f else 0.3f
isChecked = bgUtilsMethod == "server"
setOnClickListener {
if (denoIsInstalled) {
preferences.edit(commit = true) {
putString("bgutils_potoken_method", "server")
}
lifecycleScope.launch {
withContext(Dispatchers.IO) {
BgUtilsPoTokenGeneratorUtil.runServer(context) {}
}
}
} else {
Snackbar.make(requireActivity().findViewById(android.R.id.content), context.getString(R.string.please_install_package, "Deno"), Snackbar.LENGTH_SHORT).show()
post {
scriptRadio.performClick()
preferences.edit(commit = true) {
putString("bgutils_potoken_method", "server")
}
lifecycleScope.launch {
withContext(Dispatchers.IO) {
BgUtilsPoTokenGeneratorUtil.runServer(context) {}
}
}
}

@ -108,6 +108,35 @@ object MkSession {
}
val pythonBin = runtimeManager.pythonLocation.executable
val pythonExec = if (pythonBin.name.endsWith(".so")) {
"$linker \"${pythonBin.absolutePath}\""
} else {
"\"${pythonBin.absolutePath}\""
}
rcBuilder.append(
shellFunction(
"pip",
"$pythonExec -m pip"
)
)
val nodeBin = runtimeManager.nodeLocation.executable
if (nodeBin.exists()) {
val nodeExec = if (nodeBin.name.endsWith(".so")) {
"$linker \"${nodeBin.absolutePath}\""
} else {
"\"${nodeBin.absolutePath}\""
}
rcBuilder.append(
shellFunction(
"npm",
"$nodeExec ${$$"$NPM_CLI_PATH"}"
)
)
}
val ytdlpBin = runtimeManager.ytdlpPath
if (pythonBin.exists() && ytdlpBin != null && ytdlpBin.exists()) {
val pythonExec = if (pythonBin.name.endsWith(".so")) {

@ -97,27 +97,54 @@ object BgUtilsPoTokenGeneratorUtil {
progress?.invoke(ytdlResponse.err)
return Result.failure(Exception(ytdlResponse.err))
}
val hasNode = RuntimeManager.getInstance().nodeLocation.isAvailable
val hasDeno = RuntimeManager.getInstance().denoLocation.isAvailable
progress?.invoke("Downloading node-modules...")
val denoResponse = RuntimeManager.getInstance().executeDeno(
command = "install",
executeDirectory = File(serverFolder, "server")) { _, _, line ->
progress?.invoke(line)
}
if (denoResponse.exitCode != 0) {
progress?.invoke(denoResponse.err)
return Result.failure(Exception(denoResponse.err))
}
if (hasNode) {
val nodeResponse = RuntimeManager.getInstance().executeNpm(
command = "install --ignore-scripts",
executeDirectory = File(serverFolder, "server")) { _, _, line ->
progress?.invoke(line)
}
if (nodeResponse.exitCode != 0) {
progress?.invoke(nodeResponse.err)
return Result.failure(Exception(nodeResponse.err))
}
progress?.invoke("Building typescript files...")
val denoResponse2 = RuntimeManager.getInstance().executeDeno(
command = "run -A npm:typescript/tsc --outDir build",
executeDirectory = File(serverFolder, "server")) { _, _, line ->
progress?.invoke(line)
progress?.invoke("Building typescript files...")
val nodeResponse2 = RuntimeManager.getInstance().executeNode(
command = "node_modules/typescript/bin/tsc",
executeDirectory = File(serverFolder, "server")) { _, _, line ->
progress?.invoke(line)
}
if (nodeResponse2.exitCode != 0) {
progress?.invoke(nodeResponse2.err)
return Result.failure(Exception(nodeResponse2.err))
}
}
if (denoResponse2.exitCode != 0) {
progress?.invoke(denoResponse2.err)
return Result.failure(Exception(denoResponse2.err))
if (hasDeno) {
val denoResponse = RuntimeManager.getInstance().executeDeno(
command = "install",
executeDirectory = File(serverFolder, "server")) { _, _, line ->
progress?.invoke(line)
}
if (denoResponse.exitCode != 0) {
progress?.invoke(denoResponse.err)
return Result.failure(Exception(denoResponse.err))
}
progress?.invoke("Building typescript files...")
val denoResponse2 = RuntimeManager.getInstance().executeDeno(
command = "run -A npm:typescript/tsc --outDir build",
executeDirectory = File(serverFolder, "server")) { _, _, line ->
progress?.invoke(line)
}
if (denoResponse2.exitCode != 0) {
progress?.invoke(denoResponse2.err)
return Result.failure(Exception(denoResponse2.err))
}
}
if (runServerAfterwards) {

Loading…
Cancel
Save