This repository has been archived by the owner on May 29, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
140 lines (118 loc) · 3.64 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Gets the platform.
const platform = require("os").platform();
// Imports Aperture if this is macOS.
let aperture;
if (platform === "darwin") {
aperture = require("aperture")();
} else {
aperture = null;
}
// Defines if this is recording.
let recording = null;
// Gets the FFMpeg binary location.
const ffmpeg = require("ffmpeg-static-magiccap").path;
// Imports child process stuff.
const { spawn } = require("child_process");
// Used to get the temporary directory.
const tempDir = require("temp-dir");
// Defines the UUIDv4 generator.
const uuid = require("uuid/v4");
// Requires FS Nextra for filesystem stuff.
const fsNextra = require("fs-nextra");
// Starts recording.
const start = async (fps, x, y, width, height, displayInfo) => {
if (recording) {
throw new Error("Already recording.");
}
if (aperture) {
// We're on macOS! We can use the nice library by the Kap team!
const settings = {
fps: fps,
cropArea: {
x: x,
y: displayInfo.bounds.height - (y + height),
width: width,
height: height,
},
screenId: displayInfo.id,
};
await aperture.startRecording(settings);
recording = true;
} else {
// *sighs*
const tempFile = `${tempDir}/${uuid()}.mp4`;
const args = ["-y", "-video_size", `${width}x${height}`, "-framerate", fps, "-f", "x11grab", "-i", `:0.0+${x},${y}`, tempFile];
const childProcess = spawn(ffmpeg, args);
recording = [childProcess, tempFile];
}
}
// Stops recording, encodes the file as a GIF and returns the GIF as a buffer.
const stop = async mp4 => {
if (!recording) {
throw new Error("Not recording.");
}
let mp4Fp;
if (aperture) {
// Yay!
mp4Fp = await aperture.stopRecording();
recording = null;
} else {
// Boo!
const childProcess = recording[0];
mp4Fp = recording[1];
recording = null;
await new Promise(res => {
childProcess.on("close", code => {
if (code !== 0) {
throw new Error("Recording failed.");
}
res();
});
childProcess.stdin.setEncoding("utf-8");
childProcess.stdin.write("q");
});
}
// If MP4 is true, return the MP4 here.
if (mp4) {
const buffer = await fsNextra.readFile(mp4Fp);
await fsNextra.unlink(mp4Fp);
return buffer;
}
// This defines the pallete file.
const paletteFile = `${tempDir}/${uuid()}.png`;
const ffmpegPaleteGen = spawn(
ffmpeg, [
"-i", mp4Fp, "-vf", "palettegen", paletteFile
],
)
await new Promise(res => {
ffmpegPaleteGen.on("close", code => {
if (code !== 0) {
throw new Error("GIF encoding failed.");
}
res();
});
});
// We now have a MP4 file path. Time to turn it into a GIF!
const tempFile = `${tempDir}/${uuid()}.gif`;
const ffmpegProcess = spawn(
ffmpeg, [
"-i", mp4Fp, "-i", paletteFile, "-lavfi", "paletteuse", tempFile
],
);
await new Promise(res => {
ffmpegProcess.on("close", code => {
if (code !== 0) {
throw new Error("GIF encoding failed.");
}
res();
});
});
await fsNextra.unlink(mp4Fp);
await fsNextra.unlink(paletteFile);
const buffer = await fsNextra.readFile(tempFile);
await fsNextra.unlink(tempFile);
return buffer;
}
// Exports start and stop.
module.exports = {start, stop};