@nexrender/core 1.43.2 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -3
- package/readme.md +1 -0
- package/src/helpers/analytics.js +132 -0
- package/src/helpers/license.js +9 -0
- package/src/helpers/patch.js +8 -0
- package/src/index.js +39 -7
- package/src/tasks/actions.js +8 -2
- package/src/tasks/download.js +10 -7
- package/src/tasks/render.js +46 -2
- package/src/tasks/script.js +11 -0
- package/src/tasks/setup.js +8 -1
- package/test/Scripts/Startup/commandLineRenderer.jsx +1 -0
- package/test/aerender +10 -0
- package/test/empty.json +13 -0
- package/test/index.js +28 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexrender/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"author": "Inlife",
|
|
6
6
|
"scripts": {
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
"match-all": "^1.2.5",
|
|
16
16
|
"mime-types": "^2.1.29",
|
|
17
17
|
"mkdirp": "^1.0.4",
|
|
18
|
+
"nanoid": "^3.2.0",
|
|
19
|
+
"posthog-node": "^3.1.1",
|
|
18
20
|
"requireg": "^0.2.1",
|
|
19
|
-
"rimraf": "^3.0.2"
|
|
21
|
+
"rimraf": "^3.0.2",
|
|
22
|
+
"systeminformation": "^5.18.3"
|
|
20
23
|
},
|
|
21
24
|
"peerDependencies": {
|
|
22
25
|
"@nexrender/action-copy": "^1.0.0",
|
|
@@ -30,5 +33,5 @@
|
|
|
30
33
|
"publishConfig": {
|
|
31
34
|
"access": "public"
|
|
32
35
|
},
|
|
33
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "10534b5ae73c920a6ea3c5cc7c3d6ac542112319"
|
|
34
37
|
}
|
package/readme.md
CHANGED
|
@@ -71,5 +71,6 @@ Second one is responsible for mainly job-related operations of the full cycle: d
|
|
|
71
71
|
* `addLicense` - boolean, providing false will disable ae_render_only_node.txt license file auto-creation (true by default)
|
|
72
72
|
* `forceCommandLinePatch` - boolean, providing true will force patch re-installation
|
|
73
73
|
* `onInstanceSpawn` - a callback, if provided, gets called when **aerender** instance is getting spawned, with instance pointer. Can be later used to kill a hung aerender process. Callback signature: `function (instance, job, settings) {}`
|
|
74
|
+
* `noAnalytics` - boolean, enables or disables built-in fully-anonymous analytics, false by default
|
|
74
75
|
* `actions` - an object with keys corresponding to the `module` field when defining an action, value should be a function matching expected signature of an action. Used for defining actions programmatically without needing to package the action as a separate package
|
|
75
76
|
* `cache` - boolean or string. Set the cache folder used by HTTP assets. If `true` will use the default path of `${workpath}/http-cache`, if set to a string it will be interpreted as a filesystem path to the cache folder.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const os = require('os')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const crypto = require('crypto')
|
|
5
|
+
|
|
6
|
+
const si = require('systeminformation')
|
|
7
|
+
const { nanoid } = require('nanoid')
|
|
8
|
+
const {PostHog} = require('posthog-node')
|
|
9
|
+
const childProcess = require('child_process')
|
|
10
|
+
|
|
11
|
+
const { version } = require('../../package.json')
|
|
12
|
+
|
|
13
|
+
const hash = (data, salt) => crypto
|
|
14
|
+
.createHmac('md5', salt)
|
|
15
|
+
.update(data)
|
|
16
|
+
.digest('hex');
|
|
17
|
+
|
|
18
|
+
const analyticsPublicKey = 'phc_AWcZMlCOqHJiFyFKSoxT9WSrRkdKDFxpiFn8Ww0ZMHu';
|
|
19
|
+
const analytics = new PostHog(analyticsPublicKey, {
|
|
20
|
+
host: 'https://eu.posthog.com',
|
|
21
|
+
flushAt: 1,
|
|
22
|
+
flushInterval: 0,
|
|
23
|
+
disableGeoip: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A helper function to force syncronous tracking
|
|
28
|
+
*
|
|
29
|
+
* @param {*} settings
|
|
30
|
+
* @param {*} event
|
|
31
|
+
* @param {*} properties
|
|
32
|
+
*/
|
|
33
|
+
const forceSyncRequest = (settings, event, properties) => {
|
|
34
|
+
const args = JSON.stringify({settings, event, properties})
|
|
35
|
+
const proc = childProcess.fork(__filename, ['child', args], {
|
|
36
|
+
stdio: 'ignore',
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Tracking function for analytics
|
|
42
|
+
*
|
|
43
|
+
* @param {*} settings
|
|
44
|
+
* @param {*} event
|
|
45
|
+
* @param {*} properties
|
|
46
|
+
* @returns {Promise<void>}
|
|
47
|
+
*/
|
|
48
|
+
const track = async (settings, event, properties = {}, isRemote) => {
|
|
49
|
+
// if (isRemote) console.log('tracking', event, properties, settings)
|
|
50
|
+
|
|
51
|
+
if (settings.noAnalytics === true) return;
|
|
52
|
+
|
|
53
|
+
if (!settings.session) {
|
|
54
|
+
settings.session = hash(nanoid(), 'nexrender-session')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// make sure we have a unique id for this user
|
|
58
|
+
if (!settings.analyticsId) {
|
|
59
|
+
let filepath = path.join(os.homedir(), '.nexrender', 'uuid')
|
|
60
|
+
if (fs.existsSync(filepath)) {
|
|
61
|
+
settings.analyticsId = fs.readFileSync(filepath, 'utf8')
|
|
62
|
+
} else {
|
|
63
|
+
settings.analyticsId = hash(nanoid(), 'nexrender-analytics')
|
|
64
|
+
if (!fs.existsSync(path.dirname(filepath)))
|
|
65
|
+
fs.mkdirSync(path.dirname(filepath), { recursive: true })
|
|
66
|
+
|
|
67
|
+
fs.writeFileSync(filepath, settings.analyticsId)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// make sure we wait to send the event if there is an error
|
|
72
|
+
if (properties.forced === true) {
|
|
73
|
+
delete properties.forced
|
|
74
|
+
properties.$timestamp = (new Date()).toISOString();
|
|
75
|
+
return forceSyncRequest(settings, event, properties)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// collect system info
|
|
79
|
+
if (!settings.systemInfo) {
|
|
80
|
+
if (isRemote) console.log('collectiing systeminfo')
|
|
81
|
+
|
|
82
|
+
settings.systemInfo = await si.get({
|
|
83
|
+
cpu: 'manufacturer,brand,cores',
|
|
84
|
+
mem: 'total',
|
|
85
|
+
graphics: '*',
|
|
86
|
+
osInfo: 'platform,arch,distro,release',
|
|
87
|
+
dockerInfo: 'id',
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
properties.$set_once = {
|
|
91
|
+
sys_cpu_manufacturer: settings.systemInfo.cpu.manufacturer,
|
|
92
|
+
sys_cpu_brand: settings.systemInfo.cpu.brand,
|
|
93
|
+
sys_cpu_cores: settings.systemInfo.cpu.cores,
|
|
94
|
+
sys_mem_total: settings.systemInfo.mem.total,
|
|
95
|
+
sys_graphics_vendor: (settings.systemInfo.graphics.controllers[0] || []).vendor,
|
|
96
|
+
sys_graphics_model: (settings.systemInfo.graphics.controllers[0] || []).model,
|
|
97
|
+
sys_os_platform: settings.systemInfo.osInfo.platform,
|
|
98
|
+
sys_os_arch: settings.systemInfo.osInfo.arch,
|
|
99
|
+
sys_os_distro: settings.systemInfo.osInfo.distro,
|
|
100
|
+
sys_os_release: settings.systemInfo.osInfo.release,
|
|
101
|
+
sys_docker: !!settings.systemInfo.dockerInfo.id,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// anonymize job_id
|
|
106
|
+
if (properties.job_id) {
|
|
107
|
+
properties.job_id = hash(properties.job_id, settings.analyticsId)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const params = {
|
|
111
|
+
distinctId: settings.analyticsId,
|
|
112
|
+
event,
|
|
113
|
+
properties: Object.assign({
|
|
114
|
+
process: settings.process,
|
|
115
|
+
version: version,
|
|
116
|
+
debug: settings.debug,
|
|
117
|
+
$timestamp: (new Date()).toISOString(),
|
|
118
|
+
$session_id: settings.session,
|
|
119
|
+
}, properties)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
analytics.capture(params);
|
|
123
|
+
await analytics.flush();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (process.argv[2] === 'child') {
|
|
127
|
+
const args = JSON.parse(process.argv[3])
|
|
128
|
+
track(args.settings, args.event, args.properties, true)
|
|
129
|
+
.then(() => {})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = track
|
package/src/helpers/license.js
CHANGED
|
@@ -13,15 +13,24 @@ module.exports = (settings) => {
|
|
|
13
13
|
settings.logger.log(' - ' + nodefile1)
|
|
14
14
|
settings.logger.log(' - ' + nodefile2)
|
|
15
15
|
|
|
16
|
+
let applied = false
|
|
17
|
+
|
|
16
18
|
if (!fs.existsSync(adobe)) {
|
|
17
19
|
fs.mkdirSync(adobe)
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
if (!fs.existsSync(nodefile1)) {
|
|
21
23
|
fs.writeFileSync(nodefile1, '')
|
|
24
|
+
applied = true
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
if (!fs.existsSync(nodefile2)) {
|
|
25
28
|
fs.writeFileSync(nodefile2, '')
|
|
29
|
+
applied = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (applied) {
|
|
33
|
+
settings.track('Init Render License Added')
|
|
34
|
+
settings.logger.log('added render-only-node licenses for After Effects')
|
|
26
35
|
}
|
|
27
36
|
}
|
package/src/helpers/patch.js
CHANGED
|
@@ -39,8 +39,11 @@ module.exports = (settings) => {
|
|
|
39
39
|
if (patchedMatch[1] !== existingMatch[1]) {
|
|
40
40
|
try {
|
|
41
41
|
settings.logger.log('out-of-date version of the commandLineRenderer.jsx patch is detected, attepmting to update')
|
|
42
|
+
settings.track('Init Patch Update Succeeded')
|
|
42
43
|
writeTo(patched, originalFile)
|
|
43
44
|
} catch (err) {
|
|
45
|
+
settings.trackSync('Init Patch Update Failed')
|
|
46
|
+
|
|
44
47
|
if (err.code == 'EPERM') {
|
|
45
48
|
settings.logger.log('\n\n -- E R R O R --\n');
|
|
46
49
|
settings.logger.log('you need to run application with admin priviledges once');
|
|
@@ -62,6 +65,7 @@ module.exports = (settings) => {
|
|
|
62
65
|
|
|
63
66
|
if (settings.forceCommandLinePatch) {
|
|
64
67
|
settings.logger.log('forced rewrite of command line patch')
|
|
68
|
+
settings.track('Init Patch Forced')
|
|
65
69
|
writeTo(patched, originalFile)
|
|
66
70
|
}
|
|
67
71
|
} else {
|
|
@@ -75,7 +79,11 @@ module.exports = (settings) => {
|
|
|
75
79
|
settings.logger.log('patching the command line script')
|
|
76
80
|
fs.chmodSync(originalFile, '755');
|
|
77
81
|
writeTo(patched, originalFile)
|
|
82
|
+
|
|
83
|
+
settings.track('Init Patch Install Succeeded')
|
|
78
84
|
} catch (err) {
|
|
85
|
+
settings.trackSync('Init Patch Install Failed')
|
|
86
|
+
|
|
79
87
|
if (err.code == 'EPERM') {
|
|
80
88
|
settings.logger.log('\n\n -- E R R O R --\n');
|
|
81
89
|
settings.logger.log('you need to run application with admin priviledges once');
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const license = require('./helpers/license')
|
|
|
10
10
|
const autofind = require('./helpers/autofind')
|
|
11
11
|
const patch = require('./helpers/patch')
|
|
12
12
|
const state = require('./helpers/state')
|
|
13
|
+
const track = require('./helpers/analytics')
|
|
13
14
|
|
|
14
15
|
const setup = require('./tasks/setup')
|
|
15
16
|
const predownload = require('./tasks/actions')('predownload')
|
|
@@ -36,13 +37,18 @@ if (process.env.NEXRENDER_REQUIRE_PLUGINS) {
|
|
|
36
37
|
require('@nexrender/provider-sftp');
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
//
|
|
40
|
-
// https://video.stackexchange.com/questions/16706/rendered-file-with-after-effects-is-very-huge
|
|
41
|
-
//
|
|
42
|
-
|
|
43
40
|
const init = (settings) => {
|
|
44
41
|
settings = Object.assign({}, settings);
|
|
45
42
|
settings.logger = settings.logger || console;
|
|
43
|
+
settings.track = (...args) => track(settings, ...args);
|
|
44
|
+
settings.trackSync = (event, params) => track(settings, event, { forced: true, ...params })
|
|
45
|
+
|
|
46
|
+
// set default process name for analytics
|
|
47
|
+
if (!settings.process) {
|
|
48
|
+
settings.process = 'nexrender-core';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
settings.trackSync('Init Started')
|
|
46
52
|
|
|
47
53
|
// check for WSL
|
|
48
54
|
settings.wsl = isWsl
|
|
@@ -51,12 +57,16 @@ const init = (settings) => {
|
|
|
51
57
|
const binaryUser = settings.binary && fs.existsSync(settings.binary) ? settings.binary : null;
|
|
52
58
|
|
|
53
59
|
if (!binaryUser && !binaryAuto) {
|
|
60
|
+
settings.trackSync('Init Failed', { error: 'no_aerender_binary_found' })
|
|
54
61
|
throw new Error('you should provide a proper path to After Effects\' "aerender" binary')
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
if (binaryAuto && !binaryUser) {
|
|
58
65
|
settings.logger.log('using automatically determined directory of After Effects installation:')
|
|
59
66
|
settings.logger.log(' - ' + binaryAuto)
|
|
67
|
+
settings.aeBinaryStrategy = 'auto';
|
|
68
|
+
} else {
|
|
69
|
+
settings.aeBinaryStrategy = 'manual';
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
settings = Object.assign({
|
|
@@ -82,15 +92,19 @@ const init = (settings) => {
|
|
|
82
92
|
binary: binaryUser || binaryAuto,
|
|
83
93
|
})
|
|
84
94
|
|
|
95
|
+
// try to detect version
|
|
96
|
+
settings.aeBinaryVersion = (settings.binary.match(/([0-9]{4})\/Support Files/) || [])[1] || 'Unknown';
|
|
97
|
+
|
|
85
98
|
// make sure we will have absolute path
|
|
86
99
|
if (!path.isAbsolute(settings.workpath)) {
|
|
87
100
|
settings.workpath = path.join(process.cwd(), settings.workpath);
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
// if WSL, ask user to define Mapping
|
|
91
|
-
if (settings.wsl && !settings.wslMap)
|
|
104
|
+
if (settings.wsl && !settings.wslMap) {
|
|
105
|
+
settings.trackSync('Init Failed', { error: 'wsl_detected_no_map' });
|
|
92
106
|
throw new Error('WSL detected: provide your WSL drive map; ie. "Z"')
|
|
93
|
-
|
|
107
|
+
}
|
|
94
108
|
|
|
95
109
|
// add license helper
|
|
96
110
|
if (settings.addLicense) {
|
|
@@ -101,6 +115,24 @@ const init = (settings) => {
|
|
|
101
115
|
// Scripts/commandLineRenderer.jsx
|
|
102
116
|
patch(settings);
|
|
103
117
|
|
|
118
|
+
settings.trackSync('Init Succeeded', {
|
|
119
|
+
ae_binary_strategy: settings.aeBinaryStrategy,
|
|
120
|
+
ae_binary_version: settings.aeBinaryVersion,
|
|
121
|
+
ae_multi_frames: settings.multiFrames,
|
|
122
|
+
ae_max_memory_percent: !!settings.maxMemoryPercent,
|
|
123
|
+
ae_image_cache_percent: !!settings.imageCachePercent,
|
|
124
|
+
|
|
125
|
+
wsl_is_detected: !!settings.wsl,
|
|
126
|
+
wsl_map: !!settings.wslMap,
|
|
127
|
+
|
|
128
|
+
set_add_license: settings.addLicense,
|
|
129
|
+
set_force_patch: settings.forceCommandLinePatch,
|
|
130
|
+
set_skip_cleanup: settings.skipCleanup,
|
|
131
|
+
set_skip_render: settings.skipRender,
|
|
132
|
+
set_stop_onerror: settings.stopOnError,
|
|
133
|
+
set_custom_workpath: settings.workpath !== path.join(os.tmpdir(), 'nexrender'),
|
|
134
|
+
})
|
|
135
|
+
|
|
104
136
|
return settings;
|
|
105
137
|
}
|
|
106
138
|
|
|
@@ -130,5 +162,5 @@ const render = (jobConfig, settings = {}) => {
|
|
|
130
162
|
|
|
131
163
|
module.exports = {
|
|
132
164
|
init,
|
|
133
|
-
render
|
|
165
|
+
render,
|
|
134
166
|
}
|
package/src/tasks/actions.js
CHANGED
|
@@ -21,9 +21,15 @@ module.exports = actionType => (job, settings) => {
|
|
|
21
21
|
settings.logger.log(`[${job.uid}] applying ${actionType} actions...`);
|
|
22
22
|
|
|
23
23
|
return PromiseSerial((job.actions[actionType] || []).map(action => () => {
|
|
24
|
-
|
|
24
|
+
settings.track(`Job Action Started`, {
|
|
25
|
+
job_id: job.uid, // anonymized internally
|
|
26
|
+
action_type: actionType,
|
|
27
|
+
action_module: action.module,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if (settings.actions && settings.actions[action.module]) {
|
|
25
31
|
return settings.actions[action.module](job, settings, action, actionType);
|
|
26
|
-
}else{
|
|
32
|
+
} else {
|
|
27
33
|
return requireg(action.module)(job, settings, action, actionType).catch(err => {
|
|
28
34
|
return Promise.reject(new Error(`Error loading ${actionType} module ${action.module}: ${err}`));
|
|
29
35
|
});
|
package/src/tasks/download.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const url = require('url')
|
|
3
3
|
const path = require('path')
|
|
4
|
+
const requireg = require('requireg')
|
|
4
5
|
const fetch = require('make-fetch-happen')
|
|
5
6
|
const uri2path = require('file-uri-to-path')
|
|
6
7
|
const data2buf = require('data-uri-to-buffer')
|
|
7
|
-
const mime
|
|
8
|
+
const mime = require('mime-types')
|
|
8
9
|
const {expandEnvironmentVariables} = require('../helpers/path')
|
|
9
10
|
|
|
10
|
-
// TODO: redeuce dep size
|
|
11
|
-
const requireg = require('requireg')
|
|
12
|
-
|
|
13
11
|
const download = (job, settings, asset) => {
|
|
14
|
-
if (asset.type == 'data') return Promise.resolve();
|
|
15
|
-
|
|
16
12
|
// eslint-disable-next-line
|
|
17
13
|
const uri = global.URL ? new URL(asset.src) : url.parse(asset.src)
|
|
18
14
|
const protocol = uri.protocol.replace(/:$/, '');
|
|
@@ -50,6 +46,13 @@ const download = (job, settings, asset) => {
|
|
|
50
46
|
|
|
51
47
|
asset.dest = path.join(job.workpath, destName);
|
|
52
48
|
|
|
49
|
+
settings.track('Job Asset Download Started', {
|
|
50
|
+
job_id: job.uid, // anonymized internally
|
|
51
|
+
asset_type: asset.type,
|
|
52
|
+
asset_protocol: protocol,
|
|
53
|
+
asset_extension: asset.extension,
|
|
54
|
+
})
|
|
55
|
+
|
|
53
56
|
switch (protocol) {
|
|
54
57
|
/* built-in handlers */
|
|
55
58
|
case 'data':
|
|
@@ -72,7 +75,7 @@ const download = (job, settings, asset) => {
|
|
|
72
75
|
path.join(settings.workpath, "http-cache") :
|
|
73
76
|
settings.cache;
|
|
74
77
|
|
|
75
|
-
if(!asset.params) asset.params = {};
|
|
78
|
+
if (!asset.params) asset.params = {};
|
|
76
79
|
// Asset's own `params.cachePath` takes precedence (including falsy values)
|
|
77
80
|
asset.params.cachePath = Object.hasOwn(asset.params, 'cachePath') ?
|
|
78
81
|
asset.params.cachePath :
|
package/src/tasks/render.js
CHANGED
|
@@ -3,7 +3,7 @@ const path = require('path')
|
|
|
3
3
|
const {spawn} = require('child_process')
|
|
4
4
|
const {expandEnvironmentVariables, checkForWSL} = require('../helpers/path')
|
|
5
5
|
|
|
6
|
-
const progressRegex = /([\d]{1,2}:[\d]{2}:[\d]{2}:[\d]{2})\s+(\(\d
|
|
6
|
+
const progressRegex = /([\d]{1,2}:[\d]{2}:[\d]{2}:[\d]{2})\s+(\(\d+[UL]?\))/gi;
|
|
7
7
|
const durationRegex = /Duration:\s+([\d]{1,2}:[\d]{2}:[\d]{2}:[\d]{2})/gi;
|
|
8
8
|
const startRegex = /Start:\s+([\d]{1,2}:[\d]{2}:[\d]{2}:[\d]{2})/gi;
|
|
9
9
|
const nexrenderErrorRegex = /Error:\s+(nexrender:.*)$/gim;
|
|
@@ -150,6 +150,28 @@ Estimated date of change to the new behavior: 2023-06-01.\n`);
|
|
|
150
150
|
return data;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
settings.track('Job Render Started', {
|
|
154
|
+
job_id: job.uid, // anonymized internally
|
|
155
|
+
job_output_module: job.template.outputModule,
|
|
156
|
+
job_settings_template: job.template.settingsTemplate,
|
|
157
|
+
job_output_settings: job.template.outputSettings,
|
|
158
|
+
job_render_settings: job.template.renderSettings,
|
|
159
|
+
job_frame_start_set: job.template.frameStart !== undefined,
|
|
160
|
+
job_frame_end_set: job.template.frameEnd !== undefined,
|
|
161
|
+
job_frame_increment_set: job.template.frameIncrement !== undefined,
|
|
162
|
+
job_continue_on_missing: job.template.continueOnMissing,
|
|
163
|
+
job_image_sequence: job.template.imageSequence,
|
|
164
|
+
job_multi_frames: settings.multiFrames,
|
|
165
|
+
job_settings_reuse: settings.reuse,
|
|
166
|
+
job_settings_skip_render: settings.skipRender,
|
|
167
|
+
job_settings_stop_on_error: settings.stopOnError,
|
|
168
|
+
job_settings_skip_cleanup: settings.skipCleanup,
|
|
169
|
+
job_settings_max_memory_percent: !!settings.maxMemoryPercent,
|
|
170
|
+
job_settings_image_cache_percent: !!settings.imageCachePercent,
|
|
171
|
+
job_settings_aeparams_set: !!settings['aeParams'],
|
|
172
|
+
job_settings_max_render_timeout: settings.maxRenderTimeout,
|
|
173
|
+
})
|
|
174
|
+
|
|
153
175
|
// spawn process and begin rendering
|
|
154
176
|
return new Promise((resolve, reject) => {
|
|
155
177
|
renderStopwatch = Date.now();
|
|
@@ -170,6 +192,7 @@ Estimated date of change to the new behavior: 2023-06-01.\n`);
|
|
|
170
192
|
|
|
171
193
|
instance.on('error', err => {
|
|
172
194
|
clearTimeout(timeoutID);
|
|
195
|
+
settings.trackSync('Job Render Failed', { job_id: job.uid, error: 'aerender_spawn_error' });
|
|
173
196
|
return reject(new Error(`Error starting aerender process: ${err}`));
|
|
174
197
|
});
|
|
175
198
|
|
|
@@ -187,6 +210,7 @@ Estimated date of change to the new behavior: 2023-06-01.\n`);
|
|
|
187
210
|
const timeout = 1000 * settings.maxRenderTimeout;
|
|
188
211
|
timeoutID = setTimeout(
|
|
189
212
|
() => {
|
|
213
|
+
settings.trackSync('Job Render Failed', { job_id: job.uid, error: 'aerender_timeout' });
|
|
190
214
|
reject(new Error(`Maximum rendering time exceeded`));
|
|
191
215
|
instance.kill('SIGINT');
|
|
192
216
|
},
|
|
@@ -206,17 +230,30 @@ Estimated date of change to the new behavior: 2023-06-01.\n`);
|
|
|
206
230
|
settings.logger.log(fs.readFileSync(logPath, 'utf8'))
|
|
207
231
|
}
|
|
208
232
|
|
|
233
|
+
settings.trackSync('Job Render Failed', {
|
|
234
|
+
job_id: job.uid, // anonymized internally
|
|
235
|
+
exit_code: code,
|
|
236
|
+
error: 'aerender_exit_code',
|
|
237
|
+
});
|
|
238
|
+
|
|
209
239
|
clearTimeout(timeoutID);
|
|
210
240
|
return reject(new Error(outputStr || 'aerender.exe failed to render the output into the file due to an unknown reason'));
|
|
211
241
|
}
|
|
212
242
|
|
|
213
|
-
|
|
243
|
+
const renderTime = (Date.now() - renderStopwatch) / 1000
|
|
244
|
+
settings.logger.log(`[${job.uid}] rendering took ~${renderTime} sec.`);
|
|
214
245
|
settings.logger.log(`[${job.uid}] writing aerender job log to: ${logPath}`);
|
|
215
246
|
|
|
216
247
|
fs.writeFileSync(logPath, outputStr);
|
|
217
248
|
|
|
218
249
|
/* resolve job without checking if file exists, or its size for image sequences */
|
|
219
250
|
if (settings.skipRender || job.template.imageSequence || ['jpeg', 'jpg', 'png'].indexOf(outputFile) !== -1) {
|
|
251
|
+
settings.track('Job Render Finished', {
|
|
252
|
+
job_id: job.uid, // anonymized internally
|
|
253
|
+
job_finish_reason: 'skipped_check',
|
|
254
|
+
job_render_time: renderTime,
|
|
255
|
+
})
|
|
256
|
+
|
|
220
257
|
clearTimeout(timeoutID);
|
|
221
258
|
return resolve(job)
|
|
222
259
|
}
|
|
@@ -244,6 +281,7 @@ Estimated date of change to the new behavior: 2023-06-01.\n`);
|
|
|
244
281
|
settings.logger.log(fs.readFileSync(logPath, 'utf8'))
|
|
245
282
|
}
|
|
246
283
|
|
|
284
|
+
settings.trackSync('Job Render Failed', { job_id: job.uid, error: 'aerender_output_not_found' });
|
|
247
285
|
clearTimeout(timeoutID);
|
|
248
286
|
return reject(new Error(`Couldn't find a result file: ${outputFile}`))
|
|
249
287
|
}
|
|
@@ -256,6 +294,12 @@ Estimated date of change to the new behavior: 2023-06-01.\n`);
|
|
|
256
294
|
settings.logger.log(`[${job.uid}] Warning: output file size is less than 1000 bytes (${stats.size} bytes), be advised that file is corrupted, or rendering is still being finished`)
|
|
257
295
|
}
|
|
258
296
|
|
|
297
|
+
settings.track('Job Render Finished', {
|
|
298
|
+
job_id: job.uid, // anonymized internally
|
|
299
|
+
job_finish_reason: 'success',
|
|
300
|
+
job_render_time: renderTime,
|
|
301
|
+
});
|
|
302
|
+
|
|
259
303
|
clearTimeout(timeoutID);
|
|
260
304
|
resolve(job)
|
|
261
305
|
});
|
package/src/tasks/script.js
CHANGED
|
@@ -432,6 +432,17 @@ module.exports = (job, settings) => {
|
|
|
432
432
|
const base = job.workpath;
|
|
433
433
|
|
|
434
434
|
job.assets.map(asset => {
|
|
435
|
+
settings.track('Job Script Asset Wrap', {
|
|
436
|
+
job_id: job.uid, // anonymized internally
|
|
437
|
+
script_type: asset.type,
|
|
438
|
+
script_compostion_set: asset.composition !== undefined,
|
|
439
|
+
script_layer_strat: asset.layerName ? 'name' : 'index',
|
|
440
|
+
script_value_strat:
|
|
441
|
+
asset.value !== undefined ? 'value' : // eslint-disable-line no-nested-ternary, multiline-ternary
|
|
442
|
+
asset.expression !== undefined ? 'expression' : // eslint-disable-line multiline-ternary
|
|
443
|
+
undefined,
|
|
444
|
+
})
|
|
445
|
+
|
|
435
446
|
switch (asset.type) {
|
|
436
447
|
case 'video':
|
|
437
448
|
case 'audio':
|
package/src/tasks/setup.js
CHANGED
|
@@ -56,10 +56,17 @@ P.S. to prevent nexrender from removing temp file data, you also can please prov
|
|
|
56
56
|
|
|
57
57
|
// setup paths
|
|
58
58
|
job.workpath = path.join(settings.workpath, job.uid);
|
|
59
|
-
job.output = job.output || path.join(job.workpath, job.resultname);
|
|
59
|
+
job.output = job.template.output || job.output || path.join(job.workpath, job.resultname);
|
|
60
60
|
mkdirp.sync(job.workpath);
|
|
61
61
|
|
|
62
62
|
settings.logger.log(`[${job.uid}] working directory is: ${job.workpath}`);
|
|
63
63
|
|
|
64
|
+
settings.track('Job Setup Succeeded', {
|
|
65
|
+
job_id: job.uid, // anonymized internally
|
|
66
|
+
job_set_output_ext: !!job.template.outputExt,
|
|
67
|
+
job_set_output_module: !!job.template.outputModule,
|
|
68
|
+
job_set_output_image_sequence: !!job.template.imageSequence,
|
|
69
|
+
})
|
|
70
|
+
|
|
64
71
|
return Promise.resolve(job)
|
|
65
72
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Command line renderer for After Effects. (nexrender-patch-v1.0.3)
|
package/test/aerender
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
console.log('imitating rendering with aerender :p')
|
|
3
|
+
|
|
4
|
+
console.log('rendering... 0%')
|
|
5
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 1000)
|
|
6
|
+
console.log('rendering... 50%')
|
|
7
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 1000)
|
|
8
|
+
console.log('rendering... 100%')
|
|
9
|
+
|
|
10
|
+
process.exit(0)
|
package/test/empty.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"template": {
|
|
3
|
+
"src": "data:text/plain,Hello, World!",
|
|
4
|
+
"composition": "test"
|
|
5
|
+
},
|
|
6
|
+
"assets": [
|
|
7
|
+
{
|
|
8
|
+
"type": "image",
|
|
9
|
+
"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
|
|
10
|
+
"layerName": "test"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
package/test/index.js
CHANGED
|
@@ -1,2 +1,28 @@
|
|
|
1
|
-
// simple test
|
|
2
|
-
require('../src')
|
|
1
|
+
// simple test code
|
|
2
|
+
const {render} = require('../src')
|
|
3
|
+
|
|
4
|
+
process.env.NEXRENDER_ENABLE_AELOG_PROJECT_FOLDER = true
|
|
5
|
+
|
|
6
|
+
const job = {
|
|
7
|
+
template: {
|
|
8
|
+
src: 'data:text/plain,Hello, World!',
|
|
9
|
+
composition: 'test',
|
|
10
|
+
output: __dirname + '/index.js',
|
|
11
|
+
},
|
|
12
|
+
assets: [
|
|
13
|
+
{
|
|
14
|
+
type: 'image',
|
|
15
|
+
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
|
|
16
|
+
layerName: 'test',
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const settings = {
|
|
22
|
+
binary: __dirname + '/aerender',
|
|
23
|
+
workpath: __dirname + '/workpath',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
render(job, settings)
|
|
27
|
+
.then(() => console.log('rendered successfully'))
|
|
28
|
+
.catch(console.error)
|