OpenAsar/core/src/app/discord_native/browser/clips.js

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);
});