diff --git a/castro.js b/castro.js index 9ddf170..bbea79e 100644 --- a/castro.js +++ b/castro.js @@ -7,106 +7,77 @@ // Handle pause/restart // Capture frame/screenshots: (https://developer.apple.com/library/ios/qa/qa1702/_index.html) -var $ = require('nodobjc'); +const EventEmmiter = require('events'); +const $ = require('nodobjc'); $.framework('AVFoundation'); $.framework('Foundation'); -var Castro = function(){ - this._started = false; - this._used = false; - this.pool = $.NSAutoreleasePool('alloc')('init'); +function checkRect(rect) { + return typeof rect === 'object' && 'x' in rect && 'y' in rect && 'w' in rect && 'h' in rect; +} + +function getNSURL(path) { + const NSlocation = $.NSString('stringWithUTF8String', path); + return $.NSURL('fileURLWithPath', NSlocation); +} + +class Castro extends EventEmmiter { + constructor() { + super(); + this._started = false; + + this.session = $.AVCaptureSession('alloc')('init'); + + // Set the main display as capture input + this.displayId = $.CGMainDisplayID(); + + this.input = $.AVCaptureScreenInput('alloc')('initWithDisplayID', this.displayId); + this.output = $.AVCaptureMovieFileOutput('alloc')('init'); - this.session = $.AVCaptureSession('alloc')('init'); + if (this.session('canAddInput', this.input)) this.session('addInput', this.input); + if (this.session('canAddOutput', this.output)) this.session('addOutput', this.output); - // Set the main display as capture input - this.displayId = $.CGMainDisplayID(); - this.input = $.AVCaptureScreenInput('alloc')('initWithDisplayID', this.displayId); - if (this.session('canAddInput', this.input)) { - this.session('addInput', this.input); + const Delegate = $.NSObject.extend('AVCaptureFileOutputRecordingDelegate'); + Delegate.addMethod('captureOutput:didStartRecordingToOutputFileAtURL:fromConnections:', 'v@:@@@', () => { + this.emit('didStartRecording'); + }); + Delegate.addMethod('captureOutput:willFinishRecordingToOutputFileAtURL:fromConnections:error:', 'v@:@@@@', () => { + this.emit('willFinishRecording'); + }); + Delegate.register(); + this.delegate = Delegate('alloc')('init'); + + this.session('startRunning'); } - // Set a movie file as output - this.movieFileOutput = $.AVCaptureMovieFileOutput('alloc')('init'); - if (this.session('canAddOutput', this.movieFileOutput)) { - this.session('addOutput', this.movieFileOutput); + start(videoLocation, rect, captureMouseClicks, scaleFactor) { + const cropRect = checkRect(rect) ? $.CGRectMake(rect.x, rect.y, rect.w, rect.h) : null; + + if (captureMouseClicks) this.input('setCapturesMouseClicks', true); + if (cropRect) this.input('setCropRect', cropRect); + if (scaleFactor) this.input('setScaleFactor', scaleFactor); + + this.pool = $.NSAutoreleasePool('alloc')('init'); + + if (this._started) throw new Error('A recording is already in progress.'); + + this.output( + 'startRecordingToOutputFileURL', getNSURL(videoLocation), + 'recordingDelegate', this.delegate + ); + this._started = true; } - this.session('startRunning'); - this.setLocation(); -} + stop() { + if (!this._started) throw new Error('Video did not start recording.'); + + this.output('stopRecording'); + this.pool('drain'); + this._started = false; -Castro.prototype = { - - // Set recording file location - setLocation: function(path) { - // TODO: Does file exist at the file location path? If so, do something about it... - //var defaultManager = $.NSFileManager('alloc')('init') - //if (defaultManager('fileExistsAtPath',NSlocation)) { - // console.log("File already exists!") - //} - - if (!path){ - // Default Destination: e.g. "/Users/hugs/Desktop/Castro_uul3di.mov" - var homeDir = $.NSHomeDirectory(); - var desktopDir = homeDir.toString() + '/Desktop/'; - var randomString = (Math.random() + 1).toString(36).substring(12); - var filename = 'Castro_' + randomString + '.mp4'; - this.location = desktopDir + filename; - } else { - // TODO: Make sure path is legit. - this.location = path; - } - this.NSlocation = $.NSString('stringWithUTF8String', this.location); - this.NSlocationURL = $.NSURL('fileURLWithPath', this.NSlocation); - }, - - // Start recording - start: function() { - if (!this._started) { - if (!this._used) { - this.movieFileOutput('startRecordingToOutputFileURL', this.NSlocationURL, - 'recordingDelegate', this.movieFileOutput); - this._started = true; - } else { - throw new Error("Recording has completed. To make a new recording, create a new Castro object."); - } - } else { - throw new Error("A recording is already in progress."); - } - }, - - // Stop recording - stop: function() { - if (!this._used) { - if (this._started) { - this.movieFileOutput('stopRecording'); - this.pool('drain'); - this._started = false; - this._used = true; - return this.location; - } else { - throw new Error("Try starting it first!"); - } - } else { - throw new Error("Recording has completed. To make a new recording, create a new Castro object."); - } - }, - - test: function() { - console.log("Castro will record the main display for 10 seconds..."); - - console.log("Now starting..."); - this.start(); - - - setTimeout(function(_this){ - console.log("Now stopping..."); - _this.stop(); - - console.log("File location:"); - console.log(_this.location); - }, 10*1000, this); + return this.location; } } +module.exports = Castro; module.exports.Castro = Castro; diff --git a/package.json b/package.json index 784d983..6358f6e 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,14 @@ { "name": "castro", - "version": "0.0.3", + "version": "0.0.4", "description": "Automated screen recording", "author": "Jason Huggins ", - "keywords": ["video", "recording", "screencast", "osx"], + "keywords": [ + "video", + "recording", + "screencast", + "osx" + ], "homepage": "https://github.com/hugs/node-castro", "repository": { "type": "git", @@ -11,13 +16,13 @@ }, "main": "./castro.js", "engines": { - "node": "*" + "node": "*" }, "bugs": { - "url" : "https://github.com/hugs/node-castro/issues" + "url": "https://github.com/hugs/node-castro/issues" }, - "license": "MIT", + "license": "MIT", "dependencies": { - "nodobjc": "*" + "nodobjc": "*" } }