Screen capture unique file names and location
Summary: Screen captures now have unique names, so they don't get overwritten. By default they are saved to the desktop, but the path can be overwritten by setting `screenCapturePath` in `~/.sonar/config.js` Reviewed By: jknoxville Differential Revision: D9120822 fbshipit-source-id: ab6880eac475da3839f08c6e644c16bdc8693647
This commit is contained in:
committed by
Facebook Github Bot
parent
d2708d4982
commit
06e70a1555
@@ -12,17 +12,18 @@ import IOSDevice from '../devices/IOSDevice';
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import adb from 'adbkit-fb';
|
import adb from 'adbkit-fb';
|
||||||
import path from 'path';
|
|
||||||
import {exec} from 'child_process';
|
import {exec} from 'child_process';
|
||||||
|
import {remote} from 'electron';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
const SCREENSHOT_FILE_NAME = 'screen.png';
|
let CAPTURE_LOCATION = remote.app.getPath('desktop');
|
||||||
const VIDEO_FILE_NAME = 'video.mp4';
|
try {
|
||||||
const SCREENSHOT_PATH = path.join(
|
CAPTURE_LOCATION =
|
||||||
os.homedir(),
|
JSON.parse(window.process.env.CONFIG).screenCapturePath.replace(
|
||||||
'/.sonar/',
|
/^~/,
|
||||||
SCREENSHOT_FILE_NAME,
|
os.homedir(),
|
||||||
);
|
) || CAPTURE_LOCATION;
|
||||||
const VIDEO_PATH = path.join(os.homedir(), '.sonar', VIDEO_FILE_NAME);
|
} catch (e) {}
|
||||||
|
|
||||||
import type BaseDevice from '../devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
|
|
||||||
@@ -39,9 +40,9 @@ type State = {|
|
|||||||
capturingScreenshot: boolean,
|
capturingScreenshot: boolean,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
function openFile(path: string): Promise<*> {
|
function openFile(path: string): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
exec(`${getOpenCommand()} ${path}`, (error, stdout, stderr) => {
|
exec(`${getOpenCommand()} "${path}"`, (error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
@@ -63,18 +64,24 @@ function getOpenCommand(): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFileName(extension: 'png' | 'mp4'): string {
|
||||||
|
return `Screen Capture ${new Date().toISOString()}.${extension}`;
|
||||||
|
}
|
||||||
|
|
||||||
function writePngStreamToFile(stream: PullTransfer): Promise<string> {
|
function writePngStreamToFile(stream: PullTransfer): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const pngPath = path.join(CAPTURE_LOCATION, getFileName('png'));
|
||||||
stream.on('end', () => {
|
stream.on('end', () => {
|
||||||
resolve(SCREENSHOT_PATH);
|
resolve(pngPath);
|
||||||
});
|
});
|
||||||
stream.on('error', reject);
|
stream.on('error', reject);
|
||||||
stream.pipe(fs.createWriteStream(SCREENSHOT_PATH));
|
stream.pipe(fs.createWriteStream(pngPath));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScreenCaptureButtons extends Component<Props, State> {
|
class ScreenCaptureButtons extends Component<Props, State> {
|
||||||
iOSRecorder: ?any;
|
iOSRecorder: ?any;
|
||||||
|
videoPath: ?string;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
pullingData: false,
|
pullingData: false,
|
||||||
@@ -117,7 +124,7 @@ class ScreenCaptureButtons extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
captureScreenshot = () => {
|
captureScreenshot = (): ?Promise<string> => {
|
||||||
const {selectedDevice} = this.props;
|
const {selectedDevice} = this.props;
|
||||||
|
|
||||||
if (selectedDevice instanceof AndroidDevice) {
|
if (selectedDevice instanceof AndroidDevice) {
|
||||||
@@ -127,29 +134,33 @@ class ScreenCaptureButtons extends Component<Props, State> {
|
|||||||
.then(openFile)
|
.then(openFile)
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
} else if (selectedDevice instanceof IOSDevice) {
|
} else if (selectedDevice instanceof IOSDevice) {
|
||||||
exec(
|
const screenshotPath = path.join(CAPTURE_LOCATION, getFileName('png'));
|
||||||
`xcrun simctl io booted screenshot ${SCREENSHOT_PATH}`,
|
return new Promise((resolve, reject) => {
|
||||||
(err, data) => {
|
exec(
|
||||||
if (err) {
|
`xcrun simctl io booted screenshot "${screenshotPath}"`,
|
||||||
console.error(err);
|
async (err, data) => {
|
||||||
} else {
|
if (err) {
|
||||||
openFile(SCREENSHOT_PATH);
|
reject(err);
|
||||||
}
|
} else {
|
||||||
},
|
resolve(await openFile(screenshotPath));
|
||||||
);
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
startRecording = () => {
|
startRecording = () => {
|
||||||
const {selectedDevice} = this.props;
|
const {selectedDevice} = this.props;
|
||||||
|
const videoPath = path.join(CAPTURE_LOCATION, getFileName('mp4'));
|
||||||
|
this.videoPath = videoPath;
|
||||||
if (selectedDevice instanceof AndroidDevice) {
|
if (selectedDevice instanceof AndroidDevice) {
|
||||||
this.setState({
|
this.setState({
|
||||||
recording: true,
|
recording: true,
|
||||||
});
|
});
|
||||||
this.executeShell(
|
this.executeShell(
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
`screenrecord --bugreport /sdcard/${VIDEO_FILE_NAME}`,
|
`screenrecord --bugreport /sdcard/video.mp4`,
|
||||||
)
|
)
|
||||||
.then(output => {
|
.then(output => {
|
||||||
if (output) {
|
if (output) {
|
||||||
@@ -166,14 +177,14 @@ class ScreenCaptureButtons extends Component<Props, State> {
|
|||||||
(): Promise<string> => {
|
(): Promise<string> => {
|
||||||
return this.pullFromDevice(
|
return this.pullFromDevice(
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
`/sdcard/${VIDEO_FILE_NAME}`,
|
`/sdcard/video.mp4`,
|
||||||
VIDEO_PATH,
|
videoPath,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.then(openFile)
|
.then(openFile)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.executeShell(selectedDevice, `rm /sdcard/${VIDEO_FILE_NAME}`);
|
this.executeShell(selectedDevice, `rm /sdcard/video.mp4`);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -192,7 +203,7 @@ class ScreenCaptureButtons extends Component<Props, State> {
|
|||||||
recording: true,
|
recording: true,
|
||||||
});
|
});
|
||||||
this.iOSRecorder = exec(
|
this.iOSRecorder = exec(
|
||||||
`xcrun simctl io booted recordVideo ${VIDEO_PATH}`,
|
`xcrun simctl io booted recordVideo "${videoPath}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -214,16 +225,18 @@ class ScreenCaptureButtons extends Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
stopRecording = () => {
|
stopRecording = () => {
|
||||||
|
const {videoPath} = this;
|
||||||
const {selectedDevice} = this.props;
|
const {selectedDevice} = this.props;
|
||||||
|
this.videoPath = null;
|
||||||
|
|
||||||
if (selectedDevice instanceof AndroidDevice) {
|
if (selectedDevice instanceof AndroidDevice) {
|
||||||
this.executeShell(selectedDevice, `pgrep 'screenrecord' -L 2`);
|
this.executeShell(selectedDevice, `pgrep 'screenrecord' -L 2`);
|
||||||
} else if (this.iOSRecorder) {
|
} else if (this.iOSRecorder && videoPath) {
|
||||||
this.iOSRecorder.kill();
|
this.iOSRecorder.kill();
|
||||||
this.setState({
|
this.setState({
|
||||||
recording: false,
|
recording: false,
|
||||||
});
|
});
|
||||||
openFile(VIDEO_PATH);
|
openFile(videoPath);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ type Props = {
|
|||||||
/**
|
/**
|
||||||
* onClick handler.
|
* onClick handler.
|
||||||
*/
|
*/
|
||||||
onClick?: (event: SyntheticMouseEvent<>) => void,
|
onClick?: (event: SyntheticMouseEvent<>) => any,
|
||||||
/**
|
/**
|
||||||
* Whether this button is disabled.
|
* Whether this button is disabled.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user