├── capture ├── bin │ ├── nircmd.exe │ └── scrot │ │ ├── scrot │ │ └── LICENSE ├── linux.js ├── win32.js └── darwin.js ├── package.json ├── .gitignore ├── .npmignore ├── LICENSE ├── Readme.md └── module.js /capture/bin/nircmd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnvmt/node-desktop-screenshot/HEAD/capture/bin/nircmd.exe -------------------------------------------------------------------------------- /capture/bin/scrot/scrot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnvmt/node-desktop-screenshot/HEAD/capture/bin/scrot/scrot -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "desktop-screenshot", 3 | "version": "0.1.1", 4 | "description": "Cross-platform screenshot module, using external tools", 5 | "license": "MIT", 6 | "author": { 7 | "name": "John Murphy-Teixidor", 8 | "email": "johnvmt@gmail.com" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/johnvmt/node-desktop-screenshot" 13 | }, 14 | "dependencies": { 15 | "jimp": "0.2.x", 16 | "flavored-path": "0.0.x" 17 | }, 18 | "main": "module.js" 19 | } 20 | -------------------------------------------------------------------------------- /capture/linux.js: -------------------------------------------------------------------------------- 1 | module.exports = function(options, callback) { 2 | 3 | var fs = require('fs'); 4 | var childProcess = require('child_process'); 5 | var path = require('path'); 6 | 7 | var scrot = childProcess.spawn(path.join(__dirname, "bin", "scrot", "scrot"), [options.output]); 8 | scrot.on('close', function(code, signal) { 9 | try { 10 | fs.statSync(options.output); 11 | callback(null, options); // callback with options, in case options added 12 | } 13 | catch(error) { 14 | callback("file_not_found", null); 15 | } 16 | }); 17 | }; -------------------------------------------------------------------------------- /capture/win32.js: -------------------------------------------------------------------------------- 1 | module.exports = function(options, callback) { 2 | 3 | var fs = require('fs'); 4 | var childProcess = require('child_process'); 5 | var path = require('path'); 6 | 7 | var nircmd = childProcess.spawn(path.join(__dirname, "bin", "nircmd.exe"), ["savescreenshot", options.output]); 8 | 9 | nircmd.on('close', function(code, signal) { 10 | try { 11 | fs.statSync(options.output); 12 | callback(null, options); // callback with options, in case options added 13 | } 14 | catch(error) { 15 | callback("file_not_found", null); 16 | } 17 | }); 18 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | 31 | 32 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | 31 | # IntelliJ 32 | .idea 33 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 johnvmt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /capture/bin/scrot/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Tom Gilbert 2000 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, 4 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished 5 | to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies of the Software, its documentation and marketing & publicity materials, and acknowledgment shall be given 8 | in the documentation, materials and software packages that this Software was used. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN‐ 11 | FRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 12 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # node-desktop-screenshot # 2 | Take a screenshot of the computer on which Node is running, using platform-specific external tools included with the package 3 | 4 | Supports Windows (win32), OSX (darwin) and Linux platforms 5 | 6 | Windows version uses nircmd (http://nircmd.nirsoft.net) 7 | Linux version uses scrot 8 | 9 | ## Available Options ## 10 | 11 | - quality: JPEG quality (0 to 100) 12 | - width: use in conjunction with height, or by itself to maintain aspect ratio 13 | - height: use in conjunction with width, or by itself to maintain aspect ratio 14 | 15 | ## Examples ## 16 | 17 | ### Full resolution ### 18 | var screenshot = require('desktop-screenshot'); 19 | 20 | screenshot("screenshot.png", function(error, complete) { 21 | if(error) 22 | console.log("Screenshot failed", error); 23 | else 24 | console.log("Screenshot succeeded"); 25 | }); 26 | 27 | ### Resize to 400px wide, maintain aspect ratio ### 28 | 29 | var screenshot = require('desktop-screenshot'); 30 | 31 | screenshot("screenshot.png", {width: 400}, function(error, complete) { 32 | if(error) 33 | console.log("Screenshot failed", error); 34 | else 35 | console.log("Screenshot succeeded"); 36 | }); 37 | 38 | ### Resize to 400x300, set JPG quality to 60% ### 39 | 40 | var screenshot = require('desktop-screenshot'); 41 | 42 | screenshot("screenshot.jpg", {width: 400, height: 300, quality: 60}, function(error, complete) { 43 | if(error) 44 | console.log("Screenshot failed", error); 45 | else 46 | console.log("Screenshot succeeded"); 47 | }); 48 | 49 | ## TODOs ## 50 | 51 | - Tests 52 | - Multi-screen support 53 | - Cropping 54 | - Return contents of image, rather than writing file -------------------------------------------------------------------------------- /capture/darwin.js: -------------------------------------------------------------------------------- 1 | module.exports = function(options, callback) { 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var childProcess = require('child_process'); 5 | 6 | // due to bug in jpgjs processing OSX jpg images https://github.com/notmasteryet/jpgjs/issues/34 7 | // when requesting JPG capture as PNG, so JIMP can read it 8 | var ext = extension(options.output); 9 | if(ext === "jpeg" || ext === "jpg") { 10 | options.intermediate = path.resolve(path.join(__dirname, uniqueId() + ".png")); // create an intermediate file that can be processed, then deleted 11 | capture(options.intermediate, callbackReturn); 12 | } 13 | else 14 | capture(options.output, callbackReturn); // when jpegjs bug fixed, only need this line 15 | 16 | function callbackReturn(error, success) { 17 | // called from capture 18 | // callback with options, in case options added 19 | callback(error, options); 20 | } 21 | 22 | function uniqueId() { 23 | function s4() { 24 | return Math.floor((1 + Math.random()) * 0x10000) 25 | .toString(16) 26 | .substring(1); 27 | } 28 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 29 | s4() + '-' + s4() + s4() + s4(); 30 | } 31 | 32 | function extension(file) { 33 | return path.extname(file).toLowerCase().substring(1); 34 | } 35 | 36 | function capture(output, callback) { 37 | var cmd = "screencapture"; 38 | var args = [ 39 | // will create PNG by default 40 | "-t", 41 | path.extname(output).toLowerCase().substring(1), 42 | "-x", 43 | output 44 | ]; 45 | 46 | var captureChild = childProcess.spawn(cmd, args); 47 | 48 | captureChild.on('close', function(error) { 49 | if (error) 50 | callback(error.toString()); 51 | else 52 | callback(); 53 | }); 54 | 55 | captureChild.stderr.on('data', function(data) { 56 | callback(data.toString()); 57 | }); 58 | } 59 | }; -------------------------------------------------------------------------------- /module.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return new Screenshot(arguments); 3 | }; 4 | 5 | var path = require('flavored-path'); 6 | var jimp = require('jimp'); 7 | var fs = require('fs'); 8 | 9 | function Screenshot(args) { 10 | var config = this.parseArgs(args); 11 | var self = this; 12 | 13 | try { 14 | require("./capture/" + process.platform + ".js")(config.options, function(error, options) { 15 | // TODO add option for string, rather than file 16 | if(error && typeof config.callback === "function") 17 | config.callback(error, null); 18 | else if(!error) { 19 | if (typeof options.intermediate === "string") { 20 | self.processImage(options.intermediate, options.output, options, function (error, success) { 21 | fs.unlink(options.intermediate, handleCallback); // delete intermediate 22 | }); 23 | } 24 | else 25 | self.processImage(options.output, options.output, options, handleCallback); 26 | } 27 | }); 28 | } 29 | catch(error) { 30 | if(typeof error == "object" && typeof error.code === "string" && error.code === "MODULE_NOT_FOUND") 31 | handleCallback("unsupported_platform"); 32 | } 33 | 34 | function handleCallback(error, success) { 35 | if(typeof config.callback === "function") { 36 | if(typeof success === "undefined") 37 | success = !error; 38 | config.callback(error, success); 39 | } 40 | } 41 | } 42 | 43 | Screenshot.prototype.processImage = function(input, output, options, callback) { 44 | if(typeof options.width !== "number" && typeof options.height !== "number" && typeof options.quality !== "number") // no processing required 45 | callback(null); 46 | else { 47 | new jimp(input, function (err, image) { 48 | if(typeof options.width === "number") 49 | var resWidth = Math.floor(options.width); 50 | if(typeof options.height === "number") 51 | var resHeight = Math.floor(options.height); 52 | 53 | if(typeof resWidth === "number" && typeof resHeight !== "number") // resize to width, maintain aspect ratio 54 | var resHeight = Math.floor(image.bitmap.height * (resWidth / image.bitmap.width)); 55 | else if(typeof resHeight === "number" && typeof resWidth !== "number") // resize to height, maintain aspect ratio 56 | var resWidth = Math.floor(image.bitmap.width * (resHeight / image.bitmap.height)); 57 | 58 | try { 59 | image.resize(resWidth, resHeight); 60 | 61 | if(typeof options.quality === "number" && options.quality >= 0 && options.quality <= 100) 62 | image.quality(Math.floor(options.quality)); // only works with JPEGs 63 | 64 | image.write(output, callback); 65 | } 66 | catch(error) { 67 | callback(error); 68 | } 69 | }); 70 | } 71 | }; 72 | 73 | Screenshot.prototype.parseArgs = function(args) { 74 | var config = {options: {}}; 75 | 76 | for(var property in args) { 77 | if (args.hasOwnProperty(property)) { 78 | switch(typeof args[property]) { 79 | case "string": 80 | var file = args[property]; 81 | break; 82 | case "function": 83 | config.callback = args[property]; 84 | break; 85 | case "object": 86 | if(args[property] != null) 87 | config.options = args[property]; 88 | break; 89 | } 90 | } 91 | } 92 | 93 | if(typeof file === "string") 94 | config.options.output = file; 95 | 96 | if(typeof config.options.output === "string") 97 | config.options.output = path.normalize(config.options.output); 98 | 99 | return config; 100 | }; 101 | --------------------------------------------------------------------------------