ffmpeg fails when using a temporary file path for output - javascript

I'm using the fluent-ffmpeg library in Node to automatically generate a single thumbnail at the halfway mark of a given video file.
const screenshot = async (pathToFile: string) => {
// Generate a temporary file path outside of the working directory with the extension .jpg
const tempFileName = tmp.tmpNameSync({ postfix: ".jpg" });
try{
await new Promise((resolve, reject) => {
ffmpeg(pathToFile)
.thumbnail({
// This works fine when NOT using tmpNameSync
filename: tempFileName,
count: 1,
timestamps: ["50%"]
})
.on("end", resolve)
.on("error", reject);
});
} catch(err){
console.log(err);
return null;
}
return tempFileName;
};
This implementation works very well when I'm using a "non-temporary" output path, such as /path/to/thumbnail.jpg. But, when I use a library such as tmp to generate a temporary file name outside of the working directory, ffmpeg throws an error.
Error: ffmpeg exited with code 1: av_interleaved_write_frame(): Input/output error
frame= 1 fps=0.0 q=7.8 size=N/A time=00:00:00.04 bitrate=N/A speed=0.152x
frame= 1 fps=0.0 q=7.8 Lsize=N/A time=00:00:00.04 bitrate=N/A speed=0.141x
video:119kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Conversion failed!
I cannot seem to find anything about ffmpeg struggling with accessing temporary directories online, and using the command directly in the terminal works as expected, so I don't believe this to be a permissions issue. Although, I may be going about this incorrectly.
This is the full ffmpeg command that fluent-ffmpeg generates (reduced filenames so it doesn't look horrible):
ffmpeg -ss 14.118271 -i /var/folders/__/XYZ/T/tmp-XYZ/tmp-XYZ -y -filter_complex scale=w=trunc(oh*a/2)*2:h=720[size0];[size0]split=1[screen0] -vframes 1 -map [screen0] var/folders/__/XYZ/T/tmp-XYZ.jpg

After hours of debugging, I found that the problem was a result of two things.
Firstly, ffmpeg and os.tmpdir() do not mix on MacOS.
The tmpdir method generates a symlink when using MacOS instead of an absolute path, which ffmpeg doesn't seem to like. Although, this seems to be inconsistent in when it does and doesn't affect the outcome.
Regardless, the fix for this is simple.
const fixSymlinkPath = (path: string) => {
// If the current platform is MacOS, prefix the path generated with /private.
// This is the true location of the path.
return process.platform === "darwin"
? `/private${path}`
: path;
};
// Use like so.
let path = fixSymlinkPath(tmp.tmpNameSync());
Secondly, (and this is something I should've noticed earlier) fluent-ffmpeg strips leading / from the filename property, resulting in a relative path and not an absolute one.
This effectively meant that ffmpeg was outputting to a non-existent directory inside __DIRNAME.
ffmpeg(pathToFile).thumbnail({
folder: "/", // Ensure absolute path, essentially.
filename: tempFileName,
count: 1,
timestamps: ["50%"]
});
Hopefully this helps someone down the track.

Related

Angular *.docx and *.xlsx file free tool to preview inside the application using Library like 'ngx-doc-viewer2' or Other

It's been 3 days, I am searching for a solution to display my *.docx and *.xlxs file in my angular application. I have an API which is returning the files as blob. Now, I want to use that blob to show the file. I can easily download the file using window.open(blobLink), however, my main task was to view the file. So, I searched and found ngx-doc-viewer and it does not work with the blob link as I currently found and file needed to be publicly accessible. But, my application will run in the local network. So, how can I solve this problem. Here is my *.ts and HTML code below=>
TS
getFile(fileData: File) {
this.tempBlob= null;
this.ProposalService.getFile(fileData).subscribe(
(retFileData: any) => {
this.tempRetFileData = retFileData;
},
(err: Error) => {
},
() => {
const blob = new Blob([this.tempRetFileData], { type: this.contentType });
this.docURL = window.URL.createObjectURL(blob);
}
);
}
HTML
<ngx-doc-viewer [url]="docURL" viewer="google" style="width:100%;height:50vh;"></ngx-doc-viewer>
Note: Any other free Library and solution is acceptable.
For blob type use viewer="mammoth":
<ngx-doc-viewer [url]="docURL" **viewer="mammoth"** style="width:100%;height:50vh;"></ngx-doc-viewer>
To use mammoth, also add:
npm install mammoth --save
and make sure mammoth.browser.min.js is loaded. For the angular/cli you would add the following in angular.json:
"scripts": [
"node_modules/mammoth/mammoth.browser.min.js"
]

Node.js run command line (child_process?)

I have a Node.js program where I need to, on a button click, run 2 commands in the Windows command line. For example, the process I'm trying to automate by the button click would be doable manually by going to cmd and entering the following commands:
pushd \\myserver.com\folder1\folder2 //Connect to remote server folder structure
mkdir NewFolder //Create new folder in the remote folder
I've found many resources pointing that I should use 'child_process', but I'm absolutely lost when it comes to shell scripting and am having a really hard time figuring out how to do this. Here's the code I have so far:
var cp = require('child_process');
cp.exec('pushd \\\\myserver.com\\folder1\\folder2\\', { shell: '/bin/bash' }, function(err, stdout, stderr){
if(err){
console.log(err);
}
});
But this above code just returns this error (which oddly removes the '\'s from the given dir):
{ Error: Command failed: pushd \\myserver.com\folder1\folder2\
/bin/bash: line 1: pushd: \myserver.comfolder1folder2: No such file or directory
at ChildProcess.exithandler (child_process.js:281:12)
killed: false,
code: 1,
signal: null,
cmd: 'pushd \\\\myserver.com\\folder1\\folder2\\' }
/bin/bash: line 1: pushd: \myserver.comfolder1folder2: No such file or directory
I'm really lost here and would appreciate any help. Any alternative you have to child_process may also be very helpful. Thank you!
Usually all the escape sequences used for string uses '\\' for a single backslash. It is understandable you used it here for the directory path for windows.
In JS particularly '\\' doesn't exactly work like that
'abc\
def' == 'abcdef' // true
'\a' == 'a' // true
When a '\' is not followed by a character with any special meaning, it is considered to be a LineContinuation instead.
As you can see from your error output using '\\\\myserver.com' considered '\myserver.com'. Plain workaround is to use '\\\\' for single '\' or use '/' for path separation which I'm not pretty sure if shell will execute it.
This is one of the blogs explains about it in details Link.
The shell is incorrect here:
{ shell: '/bin/bash' }
It should be:
{ shell: 'CMD.EXE' }
Because in the beginning of your post, you tell that you run 2 commands in the Windows command line, which indicates you are not using Bash, which is (usually) not installed on Windows.

Get filesystem type with node js

I'm working on a project in node js and I need to get the file system type of a disk i.e., I want to know if it's FAT or NTFS etc...
Is there any way I can accomplish this with node js? Any help is appreciated . Thanks.
As you didn't specify target machine's operating system, you can use such command under Windows:
fsutil fsinfo volumeinfo <disk letter here>:
to show output simillar to this one:
H:\>fsutil fsinfo volumeinfo c:
Volume Name :
Volume Serial Number : 0x4c31bcb3
Max Component Length : 255
File System Name : NTFS
Supports Case-sensitive filenames
Preserves Case of filenames
Supports Unicode in filenames
Preserves & Enforces ACL's
Supports file-based Compression
Supports Disk Quotas
Supports Sparse files
Supports Reparse Points
Supports Object Identifiers
Supports Encrypted File System
Supports Named Streams
You need only to parse command output correctly. I would recommend you storing filesystem names in array and seeking for every in command output excluding first line. This way you can be sure which filesystem is used by target machine.
Here you have code to print what does this command output to your commandline:
const { exec } = require('child_process');
exec('fsutil fsinfo volumeinfo c:', (err, stdout, stderr) => {
if (err) {
// node couldn't execute the command
return;
}
// the *entire* stdout and stderr (buffered)
console.log(`stdout: ${stdout}`);
});
This is untested code, I really think that you want to write own snippet. I can't decide will this code work because I have no opportunity now.
Also, why would you check filesystem used?
you can use github.com/resin-io-modules/drivelist
then
const drivelist = require('drivelist');
drivelist.list((error, drives) => {
if (error) {
throw error;
}
console.log(drives);
});

Electron will-download keeps getting interrupted

I am trying to download a file, but it keeps getting interrupted, and I have no idea why. I can not find any information on how to debug the reason it got interrupted either.
Here is where I am saving the file:
C:\Users\rnaddy\AppData\Roaming\Tachyon\games\murware\super-chain-reaction\web.zip
window.webContents.session.on('will-download', (event, item, webContents) => {
let path = url.parse(item.getURL()).pathname;
let dev = path.split('/')[3] || null;
let game = path.split('/')[4] || null;
if (!dev && !game) {
item.cancel();
} else {
item.setSavePath(Settings.fileDownloadLocation(dev, game, 'web'));
item.on('updated', (event, state) => {
let progress = 0;
if (state == 'interrupted') {
console.log('Download is interrupted but can be resumed');
} else if (state == 'progressing') {
progress = item.getReceivedBytes() / item.getTotalBytes();
if (item.isPaused()) {
console.log('Download is paused');
} else {
console.log(`Received bytes: ${item.getReceivedBytes()}; Progress: ${progress.toFixed(2)}%`);
}
}
});
}
});
Here is my listener that will trigger the above:
ipcMain.on(name, (evt) => {
window.webContents.downloadURL('http://api.gamesmart.com/v2/download/murware/super-chain-reaction');
});
Here is the output that I am getting in my console:
Received bytes: 0; Progress: 0.00%
Received bytes: 233183; Progress: 0.02%
Download is interrupted but can be resumed
I have a host file setup:
127.0.0.1 api.gamesmart.com
When I try to access the path http://api.gamesmart.com/v2/download/murware/super-chain-reaction in chrome, the file downloads just fine into my Downloads folder. So, what is causing this?
If you set the specific directory for downloading, you should use full file path with the file name in item.setSavePath() method. The best way to do it, fetching the file name from downloaditem object (item in your case) itself. You can use item.getFilename() to get the name of the current download item easily. here is the doc
And also there is a good way to get frequently used public system directory paths in electron. That is, using app.getPath(name) method. name would be the pre-defined String by electron for several directories. here is the doc
So, your complete setSavePath function would be,app.getPath("downloads") + "/" + item.getFilename()
In your case, if you are OK with your file path extraction method, only thing you are missing is filename at the end of the download path.
Of course you can use any other string as the file name if you wish. But remember to put correct extension though. :)
My solution was to use the correct Windows path separator (\), .e.g. 'directory\\file.zip'. Generally, Node.js uses / for any platform, but this seems to be sensitive about the path separator.

On Node.JS, Jade Templates, and Javascript Options Objects

I am currently building a project with node.js in Windows. I am using a batch file to assemble resources and build jade templates via the command line. With Jade, I am using the switch -o to defines a JS object that fills localized content in the template
For awhile, everything worked nicely. However, changes to my JSON lookup have resulted in an error:
"The input line is too long"
Researching the error, I found that windows shell has a limit on how long your lines can be. Unfortunately, I need the whole lookup object for my project. However, I started wondering if jade can accept a path to my lookup file instead of a string with the contents of the file. Currently, I'm building the contents into a variable and calling jade with that ala:
SetLocal EnableDelayedExpansion
set content=
for /F "delims=" %%i in (%sourcedir%\assets\english.json) do set content=!content! %%i
::use the json file as a key for assembling the jade templates
call jade %sourcedir% --out %destdir% -o"%content%"
EndLocal
If I could use a path to the lookup file, it would be much easier. However, I am usure how to do that (if it's even possible). and Jade's documentation is a bit lacking.
So, in short, is it possible for Jade to accept a filepath to a JS object rather than a string containing the object? Is there a better way to contruct the jade call that wont push it past the limit?
Write a node.js script that will read your "assets" and will call a jade. Something like:
var fs = require('fs'),
_ = require('underscore'),
async = require('async');
var sourceDir = 'path to the directory with your jade templates',
destinationDir = 'path to the directory where you want the result html files to be contained in';
async.waterfall([
async.parallel.bind(null, {
serializedData: fs.readFile.bind(null, 'assets/english.json'),
files: fs.readDir.bind(null, sourceDir),
}),
function (result, callback) {
var data = JSON.parse(result.serializedData),
files = result.files;
async.parallel(_.map(files, function (file) {
return async.waterfall.bind(null, [
fs.readFile.bind(null, sourceDir + file),
function (jadeSource, callback) {
process.nextTick(callback.bind(null, jade.compile(jadeSource)(data)));
},
fs.writeFile.bind(null, destinationDir + file)
]);
}), callback);
}
], function (err) {
if (err) {
console.log("An error occured: " + err);
} else {
console.log("Done!");
}
});
Then in your batch file call this script directly, instead of enumerating the directory and calling the jade manually.
It will not only solve your problem, but also work much faster because:
I/O operations are done in parallel;
Node.js is only started once during the build process, as opposed to starting it for every single file as you do now.

Categories

Resources