wranglebot
Version:
open source media asset management
345 lines (298 loc) • 8.67 kB
text/typescript
import { v4 as uuidv4 } from "uuid";
import { SearchLite } from "searchlite";
import { MetaCopy } from "./MetaCopy.js";
import { MetaData } from "./MetaData.js";
import { Thumbnail } from "./Thumbnail.js";
import { finder } from "../system/index.js";
import DB from "../database/DB.js";
import analyseMetaFileOptions from "./analyseMetaFileOptions.js";
import { MLInterface } from "../analyse/MLInterface.js";
import CopyTool from "../media/CopyTool.js";
class MetaFile {
id;
copies: MetaCopy[] = [];
#hash;
metaData;
basename;
name;
size;
fileType;
extension;
query;
thumbnails: Thumbnail[] = [];
private _hash: any;
creationDate: Date;
/**
*
* @param options {{hash:string, id?: string, metaData?: object, name: string, basename:string, thumbnails?: string[], size: number, fileType: string, extension: string, creationDate?: string}}
*/
constructor(options) {
if (!options.hash) throw new Error("No hash provided");
this.#hash = options.hash || "NaN";
/* init id or copy from object */
this.id = options.id || uuidv4();
/* Thumbnails init */
this.thumbnails = [];
if (options.thumbnails) {
for (let thumb of options.thumbnails) {
const thumbnail = new Thumbnail(thumb);
this.thumbnails.push(thumbnail);
}
}
this.metaData = new MetaData(options.metaData) || new MetaData();
if (!options.basename) throw new Error("No basename provided");
if (options.basename.split(".").length < 2) throw new Error("Invalid basename");
this.basename = options.basename;
this.name = options.name || this.basename.split(".")[0];
this.extension = options.extension || this.basename.split(".")[1];
if (!options.size) throw new Error("No size provided");
if (!options.fileType) throw new Error("No fileType provided");
this.size = options.size;
this.fileType = options.fileType;
this.creationDate = options.creationDate ? new Date(options.creationDate) : new Date();
this._hash = this.#hash;
}
/**
* Creates a MetaFile from a source file
*
* @param source {string} the path to the source file
* @return {Promise<MetaFile>} the created MetaFile
*/
static async fromFile(source): Promise<MetaFile> {
try {
if (!finder.existsSync(source)) throw new Error("File does not exist");
const basename = finder.basename(source).toString();
const cpt = new CopyTool({
hash: "xxhash64",
});
const hash = await cpt.hashFile(source);
const metaData = await CopyTool.analyseFile(source);
const size = finder.lstatSync(source).size;
const newMf = new MetaFile({
hash,
metaData,
basename,
name: basename.substring(0, basename.lastIndexOf(".")),
size,
fileType: finder.getFileType(basename),
extension: finder.extname(basename),
});
newMf.addCopy(
new MetaCopy({
pathToSource: source,
metafile: newMf,
hash,
})
);
return newMf;
} catch (e: any) {
throw new Error("Could not create MetaFile from file: " + e.message);
}
}
public getReachableCopies(): MetaCopy[] {
let reachableCopies: MetaCopy[] = [];
for (let copy of this.copies) {
if (copy.isReachable()) {
reachableCopies.push(copy);
}
}
return reachableCopies;
}
/**
* Get Hash
* @return {string} hash
*/
get hash() {
return this.#hash;
}
async update(document, save = true) {
if (document.metaData) {
this.metaData.update(document.metaData);
}
if (document.thumbnails) {
this.thumbnails = []; //reset thumbnails
for (let thumb of document.thumbnails) {
if (thumb instanceof Object && thumb.id && thumb.data) {
this.addThumbnail(thumb);
} else {
const thumbFromDB = DB().getOne("thumbnails", { id: thumb });
if (thumbFromDB) {
this.addThumbnail(thumbFromDB);
} else {
throw new Error("Thumbnail not found in database: " + thumb);
}
}
}
}
}
/**
*
* @param thumbnail
* @returns {Thumbnail}
*/
addThumbnail(thumbnail) {
if (!thumbnail.data) throw new Error("Thumbnail data is missing");
if (!thumbnail.id) throw new Error("Thumbnail id is missing");
// if (!thumbnail.frame) throw new Error("Thumbnail frame number is missing");
const search = SearchLite.find(this.thumbnails, "id", thumbnail.id);
if (search.wasFailure()) {
const newThumbnail = new Thumbnail({
id: thumbnail.id,
data: thumbnail.data,
metaFile: this,
});
this.thumbnails.push(newThumbnail);
return newThumbnail;
} else {
// search.result.frame = thumbnail.frame;
search.result.data = thumbnail.data;
return search.result;
}
}
/**
* Removes a Thumbnail from the metafile and deletes its disk counterpart
*
* Does not remove the thumbnail from the database
*
* @param {string} thumbnailId
* @returns {boolean} True if it was successful, false otherwise
*/
removeOneThumbnail(thumbnailId) {
this.thumbnails = this.thumbnails.filter((thumbnail) => {
if (thumbnail.id === thumbnailId) {
finder.rmSync(finder.join(finder.getPathToUserData("thumbnails"), thumbnailId + ".jpg"));
return false;
}
return true;
});
}
/**
*
* @param {String} index
* @param {String} value
*/
updateMetaData(index, value) {
//update MetaData here
}
getMetaData(options = { table: false }) {
if (options.table) {
let list: any = [];
for (let [key, value] of Object.entries(this.metaData)) {
list.push(value);
}
list.push(this.creationDate);
return list;
}
return this.metaData;
}
/**
*
* @param {MetaCopy} metaCopy
*/
addCopy(metaCopy) {
for (let copy of this.copies) {
if (copy.id === metaCopy.id) {
copy = metaCopy;
return 0;
}
}
//if not found, add it
this.copies.push(metaCopy);
return 1;
}
dropCopy(metaCopy) {
const index = this.copies.indexOf(metaCopy);
if (index > -1) {
this.copies.splice(index, 1);
return -1;
}
return 0;
}
addCopies(copies) {
for (let copy of copies) {
this.addCopy(copy);
}
}
getCopiesAs(type) {
if (type === "array") {
let list: any = [];
for (let copy of this.copies) {
list.push(copy.toJSON());
}
return list;
} else if (type === "ids") {
let list: any = [];
for (let copy of this.copies) {
list.push(copy.id);
}
return list;
}
}
getMetaCopy(metaCopyId) {
if (metaCopyId) {
const search = SearchLite.find(this.copies, "id", metaCopyId);
if (search.wasSuccess()) {
return search.result;
}
return null;
} else if (this.copies.length > 0) {
for (let copy of this.copies) {
if (copy.isVerified() && copy.isReachable()) {
return copy;
}
}
} else {
return null;
}
}
getThumbnail(thumbnailId, by = "id") {
if (thumbnailId) {
const search = SearchLite.find(this.thumbnails, by, thumbnailId);
if (search.wasSuccess()) {
return search.result;
}
throw new Error("Thumbnail not found");
} else {
throw new Error("No thumbnail id provided");
}
}
getThumbnails(filters: { $ids? } = {}) {
const thumbs = this.thumbnails.map((thumbnail) => {
thumbnail.metaFile = this;
return thumbnail;
});
if (filters.$ids) {
return thumbs.filter((thumbnail) => filters.$ids.includes(thumbnail.id));
}
return thumbs;
}
analyse(options: analyseMetaFileOptions) {
if (options && options.frames) {
return MLInterface().analyseFrames({
engine: options.engine,
prompt: options.prompt,
frames: options.frames,
metafile: this,
temperature: options.temperature,
max_tokens: options.max_tokens,
});
}
throw new Error("No frames provided");
}
toJSON(options = {}) {
return {
basename: this.basename,
id: this.id,
hash: this.#hash,
creationDate: this.creationDate.toString(),
name: this.name,
fileType: this.fileType,
extension: this.extension,
size: this.size,
metaData: this.metaData.toJSON(),
copies: this.getCopiesAs("ids"),
thumbnails: this.thumbnails.map((thumbnail) => thumbnail.id),
};
}
}
export { MetaFile };