is there a way to programmatically run a shell command after a render has completed in Adobe After effects?
Execute a command line from After Effects
You can execute a command line with the sysmte.callSystem(cmdLineToExecute) function. Go see the After Effects Scripting Guide page 180 section System callSystem() method.
Here is an exemple whith a echo command : (callSystemreturns the command line return's) :
var commandOutput = system.callSystem("cmd.exe /c echo hi ");
alert(commandOutput);
This will output
hi
What is after /c is the command to execute in the cmd.
A more complex exemple from the scripting guide which get the system time :
var timeStr = system.callSystem("cmd.exe /c \"time /t\"");
alert("Current time is " + timeStr);
This will output
Curent time is 11:28
Open a command line from After Effects
Now if you are looking to open a command line in another window, you have to set the cmdLineToExecute as if you were in a command line.
On windows, if you want to open a command line in another window, you have to do this :
start cmd.exe
So if you want to do it from After
system.callSystem("cmd.exe /c start cmd.exe ");
Open a command line when the render is done in After Effects
This is a mix with #fabiantheblind's answer.
// Create a comp with a solid
var comp = app.project.items.addComp('test', 100, 100, 1, 1, 12);
comp.layers.addSolid([0.5, 0.5, 0.5], 'solid', 100, 100, 1, 1);
// Add the comp to the render queue
var rq_item = app.project.renderQueue.items.add(comp);
rq_item.outputModule(1).file = File('~/Desktop/out.mov');
rq_item.render = true;
// Set a function which will be called every frame when the comp will be rendering
// A boolean to be sure that the function called at the end is called once
var called = false;
rq_item.onStatusChanged = function() {
while (rq_item.status === RQItemStatus.RENDERING) {
// Is rendering...
$.writeln('Rendering');
}
// When the render is finished
if (!called && rq_item.status === RQItemStatus.DONE) {
called = true;
system.callSystem("cmd.exe /c start cmd.exe ");
}
};
// Launch the render
app.project.renderQueue.render();
// If something goes wrong
app.onError = function(err) {
$.writeln('ERROR ' + err);
};
Try this code. The only thing you need to investigate is why the (rq_item.status === RQItemStatus.DONE) gets called twice.
// Create a comp with a solid
var comp = app.project.items.addComp('test', 100, 100, 1, 1, 12);
comp.layers.addSolid([0.5, 0.5, 0.5], 'solid', 100, 100, 1, 1);
// Add the comp to the render queue
var rq_item = app.project.renderQueue.items.add(comp);
rq_item.outputModule(1).file = File('~/Desktop/out.mov');
rq_item.render = true;
// Set a function which will be called every frame when the comp will be rendering
// A boolean to be sure that the function called at the end is called once
var called = false;
rq_item.onStatusChanged = function() {
while (rq_item.status === RQItemStatus.RENDERING) {
// Is rendering...
$.writeln('Rendering');
}
// When the render is finished
if (!called && rq_item.status === RQItemStatus.DONE) {
called = true;
$.writeln('Done rendering');
// test for Mac or Win
var res = null;
if($.os.charAt (0) === 'M'){
res = system.callSystem('echo "Hello Mac World"');
}else{
res = system.callSystem('cmd.exe /c echo "Hello Win World"');
}
$.writeln(res);
}
};
// Launch the render
app.project.renderQueue.render();
// If something goes wrong
app.onError = function(err) {
$.writeln('ERROR ' + err);
};
Related
I am trying to add a simple countdown timer to a Phaser game. When the timer ends I want the game to restart. After adding the relevant code there are no errors in the console but I can't get the timer to appear on the screen. I am new to Phaser and am still learning Javascript. Where I am going wrong please? I have posted only the relevant code below, and the code used for already existing text in the game that is working fine (text to count coins collected).
PlayState = {};
PlayState.init = function () {
//....
};
PlayState.preload = function () {
this.game.load.image('font:numbers', 'images/numbers.png'); //for the
//HUD coin count - not the timer
};
PlayState.create = function () {
//TIMER CODE:
this.timeInSeconds = 120;
this.timeText = this.game.add.text(this.game.world.centerX,
this.game.world.centerY, "0:00",{font: '15px Arial', fill: '#FFFFFF', align:
'center'});
this.timeText.anchor.set(0.5, 0.5);
this.timer = this.game.time.events.loop(Phaser.Timer.SECOND,
this.updateTimer, this);
};
PlayState.update = function () {
this.coinFont.text = `x${this.coinPickupCount}`; //for HUD coin count not
//the timer
};
//TIMER CODE:
PlayState.updateTimer = function() {
this.timeInSeconds--;
var minutes = Math.floor(this.timeInSeconds / 60);
var seconds = this.timeInSeconds - (minutes * 60);
var timeString = this.addZeros(minutes) + ":" + this.addZeros(seconds);
this.timeText.text = timeString;
if (this.timeInSeconds == 0) {
this.game.state.restart();
}
};
//add leading zeros to any number less than 10
//for example turn 1 to 01
PlayState.addZeros = function(num) {
if (num < 10) {
num = "0" + num;
}
return num;
};
//BELOW IS CODE FOR THE COIN COUNT NOT THE TIMER
PlayState._createHud = function () {
this.keyIcon = this.game.make.image(0, 30, 'icon:key');
this.keyIcon.anchor.set(0, 0.5);
const NUMBERS_STR = '0123456789X ';
this.coinFont = this.game.add.retroFont('font:numbers', 20,
26,NUMBERS_STR, 6);
let coinIcon = this.game.make.image(this.keyIcon.width + 7,
0, 'icon:coin');
let coinScoreImg = this.game.make.image(coinIcon.x +
coinIcon.width, coinIcon.height / 2, this.coinFont);
coinScoreImg.anchor.set(0, 0.5);
this.hud = this.game.add.group();
this.hud.add(coinIcon);
this.hud.position.set(10, 10);
this.hud.add(coinScoreImg);
this.hud.add(this.keyIcon);
this.hud.fixedToCamera = true;
};
window.onload = function () {
let game = new Phaser.Game(1280, 800, Phaser.CANVAS, 'game');
game.state.add('play', PlayState);
game.state.start('play');
};
I have finally solved the issue. The text was not showing because it was being rendered AFTER the background image in create. So it was there but being hidden by the background image. I simply moved the timer code to the end of create and it now works.
PlayState.create = function () {
this.game.world.setBounds(0, 0, 2560, 800);
background1 = this.game.add.sprite(0, 0, 'background');
background2 = this.game.add.sprite(1280, 0, 'background2');
this.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
this.game.scale.setMinMax(480,320,1280,800);
this.game.scale.windowConstraints.bottom = 'visual';
this.game.add.image(0, 0, 'background');
this._loadLevel(this.game.cache.getJSON('level:1'));
this._createHud();
//TIMER CODE SHOULD GO HERE AND NOT AT THE BEGINNING OF CREATE
this.timeInSeconds = 120;
this.timeText = this.game.add.text(220, 30, "0:00",{font: '30px Arial', fill:
'#FFFFFF', align: 'center'});
this.timeText.anchor.set(0.5, 0.5);
this.timer = this.game.time.events.loop(Phaser.Timer.SECOND, this.updateTimer,
this);
};
You set the initial text to "0:00" and even that doesn't show on screen? First thing I would do is look at the coordinates where the text is located, maybe it's not visible off screen. Instead of this.game.world.centerX, this.game.world.centerY try something like 100,100, does it show up then? Also try to set very long initial text, so something like "blah test ABC 123" instead of "0:00" maybe makes some difference.
Secondly, maybe the Arial font is not available for some reason. If you leave out the {font: '15px..'center'} part it will use a default font, does that change anything?
Thirdly, you say you didn't post all your code here, but maybe you accidentally overwrite the variable this.timeText somewhere in you code? So check that you don't set that variable to be something else.
Finally, I would add a console.log to the updateTimer function, just to see if it is being called. so:
PlayState.updateTimer = function() {
console.log('updateTimer was called: ' + this.timeInSeconds);
this.timeInSeconds--;
// etc.
I am trying to write some tests in Postman (I am running the Postman Jetpacks packaged app, if it does matter) and I am facing some inconsistencies.
The scope of my test is to verify the size (width and height) of a set of images against some predefined values.
The scenario is like this: I make a call to a method that returns some urls, then I set the URLs as environment variables and then I verifying the size of each picture. Below is the code I am using in Tests tab in Postman.
tests["Status code is 200"] = responseCode.code === 200;
var data = JSON.parse(responseBody);
//test that response contains the expected attributes
tests["splash_image_url present"] = data.hasOwnProperty("splash_image_url");
tests["home_image_url present"] = data.hasOwnProperty("home_image_url");
tests["login_image_url present"] = data.hasOwnProperty("login_image_url");
tests["register_image_url present"] = data.hasOwnProperty("register_image_url");
tests["splash_logo_url present"] = data.hasOwnProperty("splash_logo_url");
tests["bar_logo_url present"] = data.hasOwnProperty("bar_logo_url");
//set each image URL as environment variable
postman.setEnvironmentVariable("splash_image_url", data.splash_image_url);
postman.setEnvironmentVariable("home_image_url", data.home_image_url);
postman.setEnvironmentVariable("login_image_url", data.login_image_url);
postman.setEnvironmentVariable("register_image_url", data.register_image_url);
postman.setEnvironmentVariable("splash_logo_url", data.splash_logo_url);
postman.setEnvironmentVariable("bar_logo_url", data.bar_logo_url);
//extract images from each URL
var splash_image_url = document.createElement("img");
splash_image_url.src = environment.splash_image_url;
var home_image_url = document.createElement("img");
home_image_url.src = environment.home_image_url;
var login_image_url = document.createElement("img");
login_image_url.src = environment.login_image_url;
var register_image_url = document.createElement("img");
register_image_url.src = environment.register_image_url;
var splash_logo_url = document.createElement("img");
splash_logo_url.src = environment.splash_logo_url;
var bar_logo_url = document.createElement("img");
bar_logo_url.src = environment.bar_logo_url;
//test the size for each picture
tests["splash_image_url width"] = splash_image_url.width === 640;
tests["splash_image_url height"] = splash_image_url.height === 960;
tests["home_image_url width"] = home_image_url.width === 640;
tests["home_image_url height"] = home_image_url.height === 960;
tests["login_image_url width"] = login_image_url.width === 640;
tests["login_image_url height"] = login_image_url.height === 960;
tests["register_image_url width"] = register_image_url.width === 640;
tests["register_image_url height"] = register_image_url.height === 960;
tests["splash_logo_url width"] = splash_logo_url.width === 310;
tests["splash_logo_url height"] = splash_logo_url.height === 80;
tests["bar_logo_url width"] = bar_logo_url.width === 155;
tests["bar_logo_url height"] = bar_logo_url.height === 40;
The problem is that sometimes when running the request all or some of the picture size verification fail. If I continue to manually run the same request again and again it will eventually show all the tests passed. This inconsistency makes the test unreliable.
I am missing something or doing something wrong? Is there a batter way to verify the picture size?
Thanks
Very nice question, was a fun challenge. Thanks !
1. Your problem
The tests actually work after you manually run them a couple of times, because the images are cached at that point.
The main issue here, is that you are not waiting for images to actually load, before checking the properties associated with them.
2. Postman Test Results
I tried a proof of concept on this by waiting for the images to actually load and found out... that my tests were not actually being displayed either as passing or failing.
This was mainly due to the way tests are run. Postman uses eval in the context of the request (more or less).
in Evaluator.js # 111
if (command === "runcode") {
try {
var result = eval(code);
event.source.postMessage({'type': 'test_result', 'result': result, 'scriptType': scriptType}, event.origin);
}
catch(e) {
console.log(e);
event.source.postMessage({'type': 'test_error', 'errorMessage': e.message, 'scriptType': scriptType}, event.origin);
}
}
Unfortunately for us, any sort of the callback logic will not get retroactively pushed back into the results chain.
2.1 Postman Secondary Test Results
It seems that posting a new set of results to event.source will not trigger a new set of results, but get completely discarded.
I have managed to find a workaround for this. Simply end the script with:
function postmanJetpacksSupportsOnlyOneResultPerTest()
{
event.source.postMessage({'type': 'test_result', 'result': tests, 'scriptType': 'test'}, event.origin);
}
throw 'ignore this. Enforcing failure on this test case, real values will come by manually calling *postmanJetpacksSupportsOnlyOneResultPerTest* when you are done with the test case.';
Then just call postmanJetpacksSupportsOnlyOneResultPerTest when all of your callbacks are done.
3. Postman Developers
I really hope you can somehow include the concept of promises. Example:
In the test runner:
var defered = new jQuery.Deferred();
tests['before timeout'] = true;
setTimeout(function() {
tests['on timeout'] = true;
defered.resolve(tests);
}, 500);
tests['after timeout'] = true;
return defered.promise();
In Evaluator.js # 113:
var result = eval(code);
if (result.promise instanceof Function) {
result.then(function(result) {
event.source.postMessage({'type': 'test_result', 'result': result, 'scriptType': scriptType}, event.origin);
});
} else {
event.source.postMessage({'type': 'test_result', 'result': result, 'scriptType': scriptType}, event.origin);
}
4. An example
// handlers
var waiting = 0;
function errorHandler() {
waiting--;
tests[this.image.name + ' load'] = false;
}
function successHandler() {
waiting--;
tests[this.image.name + ' load'] = true;
tests[this.image.name + ' width'] = this.image.width == this.width;
tests[this.image.name + ' height'] = this.image.height == this.height;
}
// test case kind of
function createImageTest(name, url, width, height)
{
// create image tag
var image = document.createElement('img');
// set the name
image.name = name;
// set error handlers
image.onerror = errorHandler.bind({
image: image
});
image.onload = successHandler.bind({
image: image,
width: width,
height: height
});
// finally attach the src
image.src = url;
// waiting on either a fail or a load
waiting++;
}
// the actual test cases
createImageTest('stackexchange logo', 'http://cdn.sstatic.net/stackexchange/img/se-logo.png', 223, 52);
createImageTest('stackoverflow logo', 'http://cdn.sstatic.net/stackoverflow/img/sprites.png', 240, 500);
// wait for all callbacks finished
(function checkFinished(){
// still images to process
if (waiting) {
// check back in another 0.1 seconds
return setTimeout(checkFinished, 100);
}
// ready to send result
postmanJetpacksSupportsOnlyOneResultPerTest();
})();
// the hack from #2.1
function postmanJetpacksSupportsOnlyOneResultPerTest()
{
event.source.postMessage({'type': 'test_result', 'result': tests, 'scriptType': 'test'}, event.origin);
}
throw 'ignore this. Enforcing failure on this test case, real values will come from init...';
Explorer, right click a single file, properties...
Shows File Properties Dialog.
I am hoping there is a direct way to do this from the command line, which explains my complete lack of research on how to do it with the win api :)
Answering my own question. I put together this script from the links provided. There is probably a more direct way -- this has a Frankenstein-like aura to it, but it works. The drawback is that there is a hack to keep the dialog open (sleep the script) that can be changed to two different methods. This script will exec any verb, as well as list the verbs.
Usage: save script as shareGUI.js, call with cscript shareGUI.js <args>
Examples:
cscript shareGUI.js C:\path\to\file.txt will list verbs
cscript shareGUI.js <verb> C:\path\to\file.txt will execute given verb
cscript shareGUI.js "Properties" C:\path\to\file.txt will show properties dialog
Include usual disclaimers in bold type.
var args = WScript.Arguments;
var usage="Usage: cscript shareGUI.js [verb] <fullpath>";
useage+="\n Function: Executes given verb on given file";
useage+="\n or lists available verbs";
useage+="\n Notes: Ampersands in property names are remove in listing";
useage+="\n Ampersands are not required when specifying verb";
useage+="\n Arguments: ";
useage+="\n verb (optional): Name of verb to be executed, case sensative";
useage+="\n if not provided, verbs are listed";
useage+="\n fullpath: FULLPATH Name of file or directory";
//
if ((args.Unnamed.length == 0) || (args.Named.Exists("?"))) {
QuitMsg(usage,0);
}
if (args.Unnamed.length > 2) {
QuitMsg(usage,0);
}
//
var tgt_;
var verb_;
if (args.Unnamed.length == 1) {
tgt_=args.Unnamed.Item(0);
verb_ = "";
}else if (args.Unnamed.length == 2) {
verb_ = args.Unnamed.Item(0);
tgt_=args.Unnamed.Item(1);
}
//
var shellApp = new ActiveXObject("Shell.Application");
QuitIfNull(shellApp,"System Error:Could not get Shell.Application ActiveXObject",2);
//
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
QuitIfNull(objFSO,"System Error:Could not get Scripting.FileSystemObject ActiveXObject",2);
//
var WSHshell = new ActiveXObject("Wscript.Shell");
QuitIfNull(WSHshell,"System Error:Could not get Wscript.Shell ActiveXObject",2);
//
var doList=verb_=="";
//
var isDir=objFSO.FolderExists(tgt_);
if (isDir) {
DoForFolder();
}else{
DoForFile();
}
WScript.Quit(0);// EXIT OK
// functions...
// redundant code prequel
function DoForFile(){
var objFile = objFSO.GetFile(tgt_);
QuitIfNull(objFile,"Error:Path \""+tgt_+"\" Not Found",3);
//
var folder = shellApp.NameSpace(objFSO.GetParentFolderName(objFile));
QuitIfNull(folder,"System Error:Could not get parent folder name of \""+tgt_+"\"",3);
//
var folderItem=folderItemFromFolder(tgt_,folder);
QuitIfNull(folderItem,"System Error:Could not folder item from parent folder of \""+tgt_+"\"",3);
//
if (doList) {
listItemVerbs(folderItem);
return;
}
var folderItemVerb=folderItemVerbFromFileItem(verb_,folderItem);
QuitIfNull(folderItemVerb,"System Error:Could not folder item verb from folder item \""+tgt_+"\"",3);
//
runTheVerb(folderItemVerb);
}
// redundant code redux
function DoForFolder(){
var folder = shellApp.NameSpace(tgt_);
QuitIfNull(folder,"System Error:Could not folder of \""+tgt_+"\"",2);
//
var folderItem=folder.Self;
QuitIfNull(folderItem,"System Error:Could not folder item of \""+tgt_+"\"",2);
//
if (doList) {
listItemVerbs(folderItem);
return;
}
var folderItemVerb=folderItemVerbFromFileItem(verb_,folderItem);
QuitIfNull(folderItemVerb,"System Error:Could not folder item verb from folder item \""+tgt_+"\"",3);
//
runTheVerb(folderItemVerb);
}
// HACK!! this part sucks, have to remain open else dialog dies
function runTheVerb(verb){
verb.DoIt();
WSH.Sleep(5000);
var focus_way=false;
if (focus_way) {
while (WSHshell.AppActivate("Properties")) {WSH.Sleep(2000)};
}else{ // forever way
WSH.Sleep(60*60*1000);// sleep for 60 minutes !
}
}
// match path and return folder item
function folderItemFromFolder(path,folder){
var folderItems = new Enumerator(folder.Items());
for (; ! folderItems.atEnd(); folderItems.moveNext()) {
var folderItem = folderItems.item();
if (folderItem.Path==tgt_) {
//WScript.Echo("tgt_:"+folderItem.Path);
//WScript.Quit(0);
return folderItem
}
}
return null;
}
// match verb name
function folderItemVerbFromFileItem(verb,folderItem){
verb=verb.replace('&','');
var folderItemVerbs = new Enumerator(folderItem.Verbs());
for (; ! folderItemVerbs.atEnd(); folderItemVerbs.moveNext()) {
var folderItemVerb = folderItemVerbs.item();
//WScript.echo("Verb:"+folderItemVerb );
var fix_n=folderItemVerb.Name.replace('&','');
if (fix_n == verb) {
return folderItemVerb;
}
}
return null;
}
// echo the verbs, ignore blanks, remove ampersands
function listItemVerbs(folderItem){
var folderItemVerbs = new Enumerator(folderItem.Verbs());
for (; ! folderItemVerbs.atEnd(); folderItemVerbs.moveNext()) {
var folderItemVerb = folderItemVerbs.item();
var fix_n=folderItemVerb.Name.replace('&','');
if (fix_n!="") {
WScript.echo(""+fix_n );
}
}
}
function QuitIfNull(who,why,how){
if (who==null) {
QuitMsg(why,how);
}
}
function QuitMsg(why,how){
WScript.Echo(why);
WScript.Quit(how);
}
I'm trying to print out text when a user touches a sprite, however, even though I get no errors when building and running the code, the code refuses to printout the text and I cannot understand what I am doing wrong. Please help me understand why it does not print.
It prints the debug text when I touch the sprite, but not the GUI text with total.
This is the code:
#pragma strict
function Start () {
OnGUI();
}
function OnGUI(){
//var total = 0;
//GUI.Label( myRect, total.ToString() ); // Displays "10".
//GUI.Label( myRect, "" + bullets ); // Displays "10".
// GUI.Label(Rect(0,0,Screen.width,Screen.height),"Total:"+total);
}
//function Update () {
//}
var platform : RuntimePlatform = Application.platform;
function Update(){
if(platform == RuntimePlatform.Android || platform == RuntimePlatform.IPhonePlayer){
if(Input.touchCount > 0) {
if(Input.GetTouch(0).phase == TouchPhase.Began){
checkTouch(Input.GetTouch(0).position);
}
}
}else if(platform == RuntimePlatform.WindowsEditor){
if(Input.GetMouseButtonDown(0)) {
checkTouch(Input.mousePosition);
}
}
}
function checkTouch(pos){
var total = 0;
var wp : Vector3 = Camera.main.ScreenToWorldPoint(pos);
var touchPos : Vector2 = new Vector2(wp.x, wp.y);
var hit = Physics2D.OverlapPoint(touchPos);
if(hit){
GUI.Label(Rect(0,0,Screen.width,Screen.height),"Total:");
Debug.Log(hit.transform.gameObject.name);
hit.transform.gameObject.SendMessage('Clicked',0,SendMessageOptions.DontRequireReceiver);
//total = total +1;
}
}
You cannot put a gui item outside the OnGUI Method. You need to set a boolean to turn on the label.
Something along these lines.
OnGUI(){
if(labelbool)
GUI.Label ....
}
check(){
labelbool = true;
}
I have question about my code.
It is really working weird.... I have no clue why this happens but let me explain. (MY English is bad but I will try my best to explain and give you guys good picture what is going on)
var spaceshipIncoming = document.createElement('audio');
var canvasMS12Main = document.getElementById('canvasMS12Main');
var imgMainMs12 = canvasMS12Main.getContext('2d');
var mainMs12 = new Image();
var number2 = 5;
var formatImage = ".png";
var stopeIncreaseMS12 = false;
function introMenu(){ //509,734,306,345
imgMain.drawImage(mainImg, 6, 8, gameWidth, gameHeight, 0, 0, gameWidth, gameHeight);
audioElement.setAttribute('src', 'soma.mp3');
audioElement.play();
}
function ms12AniAndSound(){
setTimeout("introMS12()",5000);
spaceshipIncoming.setAttribute('src', 'spaceshipIncoming.mp3');
spaceshipIncoming.play();
}
function introMS12(){//78
if(number2 < 78){
number2 = number2 + 1;
}else if (number2 == 78){
stopeIncreaseMS12 = true;
}
var num = new Number(number2);
mainMs12.src = 'ms12IntroAnimation/introMS12-' + num + formatImage;
clearMainMS12();
imgMainMs12.drawImage(mainMs12, 0, 0, gameWidth, gameHeight, 0, 0, gameWidth, gameHeight);
delayMS12();
}
function delayMS12(){
if(stopeIncreaseMS12 == false){
setTimeout("introMS12()",25);
}else if(stopeIncreaseMS12 == true){
clearMainMS12();
}
}
function clearMainMS12(){
imgMainMs12.clearRect(0, 0, 800, 500);
}
I have folder that has animation images, and the each PNG files are named like: introMS12-#. and each # starts from 6 to 78 (so basically it looks like introMS12-6.png, introMS12-7.png, introMS12-8.png.....introMS12-78.png)
When I first load the webpage, it doesn't show animation, but plays only mp3 sound on canvas. However, if I refresh it or press F5, it plays animation..... sometimes it doesn't play animation even if I refresh it. However, if I close the Chrome web browser and restart it and try refresh button, then it starts playing animation..... (it works randomly....)
I have been trying to make this work over 10 hours... but I do not know what is wrong with my code...... pleas help me ! thanks.