Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Replace occurrences of the deprecated errorAndThrow API #322

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/commands/applescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ export async function macosExecAppleScript (opts = {}) {
timeout,
} = opts;
if (!script && !command) {
this.log.errorAndThrow('AppleScript script/command must not be empty');
throw this.log.errorWithException('AppleScript script/command must not be empty');
}
if (/\n/.test(/** @type {string} */(command))) {
this.log.errorAndThrow('AppleScript commands cannot contain line breaks');
throw this.log.errorWithException('AppleScript commands cannot contain line breaks');
}
// 'command' has priority over 'script'
const shouldRunScript = !command;
Expand Down
77 changes: 52 additions & 25 deletions lib/commands/record-screen.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import _ from 'lodash';
import { waitForCondition } from 'asyncbox';
import { util, fs, net, tempDir } from 'appium/support';
import log from '../logger';
import { SubProcess } from 'teen_process';
import B from 'bluebird';

Expand All @@ -17,6 +16,7 @@ const DEFAULT_PRESET = 'veryfast';

/**
*
* @this {Mac2Driver}
* @param {string} localFile
* @param {string?} remotePath
* @param {import('@appium/types').StringRecord} [uploadOptions={}]
Expand All @@ -25,7 +25,7 @@ const DEFAULT_PRESET = 'veryfast';
async function uploadRecordedMedia (localFile, remotePath = null, uploadOptions = {}) {
if (_.isEmpty(remotePath) || _.isNil(remotePath)) {
const {size} = await fs.stat(localFile);
log.debug(`The size of the resulting screen recording is ${util.toReadableSizeString(size)}`);
this.log.debug(`The size of the resulting screen recording is ${util.toReadableSizeString(size)}`);
return (await util.toInMemoryBase64(localFile)).toString();
}

Expand All @@ -43,17 +43,29 @@ async function uploadRecordedMedia (localFile, remotePath = null, uploadOptions
return '';
}

async function requireFfmpegPath () {
/**
* @param {import('@appium/types').AppiumLogger} log
*/
async function requireFfmpegPath (log) {
try {
return await fs.which(FFMPEG_BINARY);
} catch (e) {
log.errorAndThrow(`${FFMPEG_BINARY} has not been found in PATH. ` +
`Please make sure it is installed`);
throw log.errorWithException(
`${FFMPEG_BINARY} has not been found in PATH. ` +
`Please make sure it is installed`
);
}
}

class ScreenRecorder {
constructor (videoPath, opts = {}) {
/**
*
* @param {string} videoPath
* @param {import('@appium/types').AppiumLogger} log
* @param {ScreenRecorderOptions} opts
*/
constructor (videoPath, log, opts) {
this._log = log;
this._videoPath = videoPath;
this._process = null;
this._fps = (opts.fps && opts.fps > 0) ? opts.fps : DEFAULT_FPS;
Expand All @@ -77,7 +89,7 @@ class ScreenRecorder {

async _enforceTermination () {
if (this._process && this.isRunning()) {
log.debug('Force-stopping the currently running video recording');
this._log.debug('Force-stopping the currently running video recording');
try {
await this._process.stop('SIGKILL');
} catch (ign) {}
Expand All @@ -91,16 +103,17 @@ class ScreenRecorder {
}

async start () {
const ffmpeg = await requireFfmpegPath();
const ffmpeg = await requireFfmpegPath(this._log);

/** @type {string[]} */
const args = [
'-loglevel', 'error',
'-t', `${this._timeLimit}`,
'-f', 'avfoundation',
...(this._captureCursor ? ['-capture_cursor', '1'] : []),
...(this._captureClicks ? ['-capture_mouse_clicks', '1'] : []),
'-framerate', `${this._fps}`,
'-i', this._deviceId,
'-i', `${this._deviceId}`,
'-vcodec', 'libx264',
'-preset', this._preset,
'-tune', 'zerolatency',
Expand All @@ -112,25 +125,26 @@ class ScreenRecorder {
...(this._videoFilter ? ['-filter:v', this._videoFilter] : []),
];

/** @type {string[]} */
const fullCmd = [
ffmpeg,
...args,
this._videoPath,
];
this._process = new SubProcess(fullCmd[0], fullCmd.slice(1));
log.debug(`Starting ${FFMPEG_BINARY}: ${util.quote(fullCmd)}`);
this._log.debug(`Starting ${FFMPEG_BINARY}: ${util.quote(fullCmd)}`);
this._process.on('output', (stdout, stderr) => {
if (_.trim(stdout || stderr)) {
log.debug(`[${FFMPEG_BINARY}] ${stdout || stderr}`);
this._log.debug(`[${FFMPEG_BINARY}] ${stdout || stderr}`);
}
});
this._process.once('exit', async (code, signal) => {
this._process = null;
if (code === 0) {
log.debug('Screen recording exited without errors');
this._log.debug('Screen recording exited without errors');
} else {
await this._enforceTermination();
log.warn(`Screen recording exited with error code ${code}, signal ${signal}`);
this._log.warn(`Screen recording exited with error code ${code}, signal ${signal}`);
}
});
await this._process.start(0);
Expand All @@ -149,10 +163,12 @@ class ScreenRecorder {
});
} catch (e) {
await this._enforceTermination();
log.errorAndThrow(`The expected screen record file '${this._videoPath}' does not exist. ` +
`Check the server log for more details`);
throw this._log.errorWithException(
`The expected screen record file '${this._videoPath}' does not exist. ` +
`Check the server log for more details`
);
}
log.info(`The video recording has started. Will timeout in ${util.pluralize('second', this._timeLimit, true)}`);
this._log.info(`The video recording has started. Will timeout in ${util.pluralize('second', this._timeLimit, true)}`);
}

async stop (force = false) {
Expand All @@ -161,7 +177,7 @@ class ScreenRecorder {
}

if (!this.isRunning()) {
log.debug('Screen recording is not running. Returning the recent result');
this._log.debug('Screen recording is not running. Returning the recent result');
return await this.getVideoPath();
}

Expand Down Expand Up @@ -215,12 +231,12 @@ export async function startRecordingScreen (options) {
}

if (this._screenRecorder?.isRunning?.()) {
log.debug('The screen recording is already running');
this.log.debug('The screen recording is already running');
if (!forceRestart) {
log.debug('Doing nothing');
this.log.debug('Doing nothing');
return;
}
log.debug('Forcing the active screen recording to stop');
this.log.debug('Forcing the active screen recording to stop');
await this._screenRecorder.stop(true);
}
this._screenRecorder = null;
Expand All @@ -229,7 +245,7 @@ export async function startRecordingScreen (options) {
prefix: util.uuidV4().substring(0, 8),
suffix: `.${DEFAULT_EXT}`,
});
this._screenRecorder = new ScreenRecorder(videoPath, {
this._screenRecorder = new ScreenRecorder(videoPath, this.log, {
fps: parseInt(`${fps}`, 10),
timeLimit: parseInt(`${timeLimit}`, 10),
preset,
Expand Down Expand Up @@ -260,19 +276,30 @@ export async function startRecordingScreen (options) {
*/
export async function stopRecordingScreen (options = {}) {
if (!this._screenRecorder) {
log.debug('No screen recording has been started. Doing nothing');
this.log.debug('No screen recording has been started. Doing nothing');
return '';
}

log.debug('Retrieving the resulting video data');
this.log.debug('Retrieving the resulting video data');
const videoPath = await this._screenRecorder.stop();
if (!videoPath) {
log.debug('No video data is found. Returning an empty string');
this.log.debug('No video data is found. Returning an empty string');
return '';
}
return await uploadRecordedMedia(videoPath, options.remotePath, options);
return await uploadRecordedMedia.bind(this)(videoPath, options.remotePath, options);
};

/**
* @typedef {import('../driver').Mac2Driver} Mac2Driver
*/

/**
* @typedef {Object} ScreenRecorderOptions
* @property {number} [fps]
* @property {string|number} deviceId
* @property {string} [preset]
* @property {boolean} [captureCursor]
* @property {boolean} [captureClicks]
* @property {string} [videoFilter]
* @property {number} [timeLimit]
*/
Loading