134 lines
4.2 KiB
JavaScript
134 lines
4.2 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var _buffer = _interopRequireDefault(require("buffer"));
|
||
|
var _promises = _interopRequireDefault(require("fs/promises"));
|
||
|
var _path = _interopRequireDefault(require("path"));
|
||
|
var _DiscordIPC = require("../common/DiscordIPC");
|
||
|
var _fileutils = require("../common/fileutils");
|
||
|
var _utils = require("../common/utils");
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
/* eslint-disable no-console */
|
||
|
|
||
|
const MAX_LENGTH = _buffer.default.constants.MAX_LENGTH;
|
||
|
const DISCORD_HEADER_NAME = 'dscl';
|
||
|
const INVALID_FILE_ERROR = 'Invalid file';
|
||
|
class InvalidFileError extends Error {}
|
||
|
function verifyIsMP4(buffer) {
|
||
|
if (getBoxHeaderName(buffer, 0) !== 'ftyp') {
|
||
|
throw new InvalidFileError(INVALID_FILE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
function verifyHasMP4Extension(filename) {
|
||
|
if (_path.default.parse(filename).ext !== '.mp4') {
|
||
|
throw new InvalidFileError(INVALID_FILE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
function getBoxSize(buffer, startIndex) {
|
||
|
return buffer.readUInt32BE(startIndex);
|
||
|
}
|
||
|
function getBoxHeaderName(buffer, startIndex) {
|
||
|
return buffer.toString('ascii', startIndex + 4, startIndex + 8);
|
||
|
}
|
||
|
function verifyValidClip(buffer) {
|
||
|
let currIndex = 0;
|
||
|
while (currIndex < buffer.byteLength) {
|
||
|
const boxHeaderName = getBoxHeaderName(buffer, currIndex);
|
||
|
if (boxHeaderName === DISCORD_HEADER_NAME) {
|
||
|
return;
|
||
|
}
|
||
|
const boxSize = getBoxSize(buffer, currIndex);
|
||
|
//box size must be at least 8 to account for header, so return false for malformed file
|
||
|
if (boxSize < 8) {
|
||
|
throw new InvalidFileError(INVALID_FILE_ERROR);
|
||
|
}
|
||
|
currIndex += boxSize;
|
||
|
}
|
||
|
throw new InvalidFileError(INVALID_FILE_ERROR);
|
||
|
}
|
||
|
async function loadClip(filename) {
|
||
|
try {
|
||
|
verifyHasMP4Extension(filename);
|
||
|
const result = await (0, _fileutils.readFulfilledFiles)([filename], MAX_LENGTH, true);
|
||
|
const buffer = result[0].data;
|
||
|
verifyIsMP4(buffer);
|
||
|
verifyValidClip(buffer);
|
||
|
return result[0];
|
||
|
} catch (e) {
|
||
|
if (e instanceof InvalidFileError) {
|
||
|
console.log(`Invalid clips file: ${e}`);
|
||
|
} else {
|
||
|
console.error(`Invalid clips file: ${e}`);
|
||
|
}
|
||
|
throw new Error(INVALID_FILE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
async function getClipMetadata(filename, dirPath) {
|
||
|
try {
|
||
|
verifyHasMP4Extension(filename);
|
||
|
} catch (e) {
|
||
|
return null;
|
||
|
}
|
||
|
const filepath = _path.default.join(dirPath, filename);
|
||
|
const handle = await _promises.default.open(filepath, 'r');
|
||
|
const stats = await handle.stat();
|
||
|
let currIndex = 0;
|
||
|
const mp4HeaderBuffer = Buffer.alloc(8);
|
||
|
try {
|
||
|
await handle.read({
|
||
|
buffer: mp4HeaderBuffer,
|
||
|
position: 0
|
||
|
});
|
||
|
verifyIsMP4(mp4HeaderBuffer);
|
||
|
currIndex += getBoxSize(mp4HeaderBuffer, currIndex);
|
||
|
while (currIndex < stats.size) {
|
||
|
await handle.read({
|
||
|
buffer: mp4HeaderBuffer,
|
||
|
position: currIndex
|
||
|
});
|
||
|
const boxSize = getBoxSize(mp4HeaderBuffer, 0);
|
||
|
if (boxSize < 8) {
|
||
|
return null;
|
||
|
}
|
||
|
const header = getBoxHeaderName(mp4HeaderBuffer, 0);
|
||
|
if (header === DISCORD_HEADER_NAME) {
|
||
|
const metadataBuffer = Buffer.alloc(boxSize - 8);
|
||
|
await handle.read({
|
||
|
buffer: metadataBuffer,
|
||
|
position: currIndex + 8
|
||
|
});
|
||
|
const metadata = JSON.parse(metadataBuffer.toString('utf-8'));
|
||
|
return {
|
||
|
filepath: filepath,
|
||
|
metadata: metadata
|
||
|
};
|
||
|
}
|
||
|
currIndex += boxSize;
|
||
|
}
|
||
|
return null;
|
||
|
} catch (e) {
|
||
|
console.log(`error: ${e}`);
|
||
|
return null;
|
||
|
} finally {
|
||
|
await handle.close();
|
||
|
}
|
||
|
}
|
||
|
async function deleteClip(path) {
|
||
|
try {
|
||
|
await loadClip(path);
|
||
|
await (0, _fileutils.deleteFile)(path);
|
||
|
} catch (e) {
|
||
|
console.log(`Invalid clips file to delete: ${e}`);
|
||
|
throw new Error(INVALID_FILE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
_DiscordIPC.DiscordIPC.main.handle(_DiscordIPC.IPCEvents.LOAD_CLIP, (_, path) => {
|
||
|
return loadClip(path);
|
||
|
});
|
||
|
_DiscordIPC.DiscordIPC.main.handle(_DiscordIPC.IPCEvents.LOAD_CLIPS_DIRECTORY, async (_, dirPath) => {
|
||
|
const filenames = await (0, _fileutils.getFilesnamesFromDirectory)(dirPath);
|
||
|
const filteredFiles = (await Promise.all(filenames.map(filename => getClipMetadata(filename, dirPath)))).filter(_utils.isNotNullish);
|
||
|
return filteredFiles;
|
||
|
});
|
||
|
_DiscordIPC.DiscordIPC.main.handle(_DiscordIPC.IPCEvents.DELETE_CLIP, (_, path) => {
|
||
|
return deleteClip(path);
|
||
|
});
|