fluent-file 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -49
- package/dist/index.d.ts +486 -253
- package/dist/index.js +1 -10
- package/package.json +18 -71
package/README.md
CHANGED
|
@@ -1,68 +1,85 @@
|
|
|
1
1
|
# Fluent File
|
|
2
2
|
A fluent TypeScript library for working with files and folders
|
|
3
3
|
|
|
4
|
-
##
|
|
5
|
-
|
|
4
|
+
## FluentFolder
|
|
5
|
+
|
|
6
|
+
### Fields:
|
|
6
7
|
- `path`: the absolute path to this folder (`/tmp/example/someFolder`)
|
|
7
8
|
- `name`: the last piece of the absolute path (`someFolder`)
|
|
8
|
-
- `
|
|
9
|
-
|
|
9
|
+
- `info`: an object with both `path` and `name`
|
|
10
|
+
|
|
11
|
+
### Methods:
|
|
12
|
+
- `stats()`
|
|
13
|
+
- `exists()`
|
|
14
|
+
- `ensureExists()`
|
|
15
|
+
- `ensureEmpty()`
|
|
16
|
+
- `remove()`
|
|
17
|
+
- `findFolders()`
|
|
18
|
+
- `findFiles()`
|
|
19
|
+
|
|
20
|
+
### Transform methods:
|
|
21
|
+
- `file()`: create `FluentFile` by resolving the path from this `FluentFolder`
|
|
22
|
+
- `folder()`: create `FluentFolder` by resolving the path from this `FluentFolder`
|
|
10
23
|
|
|
11
|
-
Provides folder methods:
|
|
12
|
-
- exists()
|
|
13
|
-
- ensureExists()
|
|
14
|
-
- ensureEmpty()
|
|
15
|
-
- delete()
|
|
16
24
|
|
|
17
|
-
##
|
|
18
|
-
|
|
25
|
+
## FluentFile
|
|
26
|
+
|
|
27
|
+
### Fields:
|
|
19
28
|
- `path`: the absolute path to this file (`/tmp/example/someFolder/someFile.txt`)
|
|
20
|
-
- `
|
|
21
|
-
- `
|
|
29
|
+
- `folderPath`: the absolute path to the folder this file is in (`/tmp/example/someFolder`)
|
|
30
|
+
- `info`: an object with `absolutePath`, `folderPath`, `name`, `basename` and `ext`
|
|
31
|
+
|
|
32
|
+
### Getters/setters (call without arg to get, call with arg to set):
|
|
33
|
+
- `name`: the name of the file **with** the extension (`someFile.txt`)
|
|
34
|
+
- `basename`: the name of the file **without** the extension (`someFile`)
|
|
22
35
|
- `ext`: the file extension **without** the leading dot. (`txt`)
|
|
23
|
-
- `parentName`: the 2nd to last piece of the absolute path to this file (`someFolder`)
|
|
24
|
-
- `parentPath`: the absolute path to the parent of this file (`/tmp/example/someFolder`)
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
- `
|
|
28
|
-
|
|
37
|
+
### Schema methods:
|
|
38
|
+
- `schema()`: create new `FluentFile` instance with a [Standard Schema](https://standardschema.dev)
|
|
39
|
+
|
|
40
|
+
### Info methods:
|
|
41
|
+
- `stats()` calls `node:fs/promises.stat()`
|
|
29
42
|
- `exists()`
|
|
30
43
|
- `ensureExists()`
|
|
44
|
+
|
|
45
|
+
### Move/remove methods:
|
|
31
46
|
- `copyTo()`
|
|
32
47
|
- `moveTo()`
|
|
33
48
|
- `linkTo()`
|
|
34
49
|
- `symlinkTo()`
|
|
35
|
-
- `
|
|
50
|
+
- `remove()`
|
|
51
|
+
|
|
52
|
+
### Read methods:
|
|
36
53
|
- `readText()`
|
|
37
|
-
- `
|
|
54
|
+
- `readBuffer()`
|
|
55
|
+
- `read()`: makes use of this file's `schema` to parse then validate the file contents
|
|
56
|
+
- `createReadStream()`
|
|
57
|
+
|
|
58
|
+
### Write methods:
|
|
59
|
+
- `append()`
|
|
38
60
|
- `writeText()`
|
|
61
|
+
- `writeBuffer()`
|
|
62
|
+
- `write()`: makes use of this file's `schema` to validate then stringify the file contents
|
|
63
|
+
- `createWriteStream()`
|
|
64
|
+
|
|
65
|
+
### Image methods:
|
|
66
|
+
|
|
67
|
+
Uses [sharp](https://github.com/lovell/sharp) library
|
|
68
|
+
|
|
69
|
+
- `metadata()`
|
|
70
|
+
- `resize()`
|
|
71
|
+
- `toAVIF()`
|
|
72
|
+
- `phash()`: uses [sharp-phash](https://www.npmjs.com/package/sharp-phash) to create a [perceptual hash](https://en.wikipedia.org/wiki/Perceptual_hashing) of the image
|
|
73
|
+
- plus all other `sharp` methods
|
|
74
|
+
|
|
75
|
+
### Video methods:
|
|
76
|
+
|
|
77
|
+
Uses [ffmpeg](https://www.ffmpeg.org/) commands
|
|
78
|
+
|
|
79
|
+
- `metadata()`: using [ffprobe](https://ffmpeg.org/ffprobe.html)
|
|
80
|
+
- `extractFrame()`
|
|
81
|
+
|
|
82
|
+
### Transform methods:
|
|
83
|
+
- `file()`: create `FluentFile` by resolving the path from this `FluentFile`
|
|
84
|
+
- `folder()`: create `FluentFolder` by resolving the path from this `FluentFile`
|
|
39
85
|
|
|
40
|
-
## JsonFile
|
|
41
|
-
Extends [AFile](#afile)
|
|
42
|
-
- the `constructor()` takes a [zod](https://zod.dev) schema
|
|
43
|
-
- adds a `read()` method that reads the file as text, JSON parses it, then validates it using the zod schema
|
|
44
|
-
- adds a `write(contents)` method that validates the contents using the zod schema, JSON stringifies it, then writes the text
|
|
45
|
-
|
|
46
|
-
## YamlFile
|
|
47
|
-
Extends [JsonFile](#jsonfile)
|
|
48
|
-
- overrides `read()` to use `YAML.parse` instead of `JSON.parse`, but still validates using the zod schema
|
|
49
|
-
- overrides `write(contents)` to use `YAML.stringify` instead of `JSON.stringify`, but still validates using the zod schema
|
|
50
|
-
|
|
51
|
-
## ImageFile
|
|
52
|
-
Extends [AFile](#afile)
|
|
53
|
-
- adds a [sharp](https://github.com/lovell/sharp) property
|
|
54
|
-
- adds a `convertToAvif()` method
|
|
55
|
-
- adds a `getPhash()` method using [sharp-phash](https://www.npmjs.com/package/sharp-phash) to create a [perceptual hash](https://en.wikipedia.org/wiki/Perceptual_hashing) of the image
|
|
56
|
-
|
|
57
|
-
## VideoFile
|
|
58
|
-
Extends [AFile](#afile)
|
|
59
|
-
- adds a [ffmpeg](http://www.ffmpeg.org/) property, provided by [fluent-ffmpeg](https://www.npmjs.com/package/fluent-ffmpeg)
|
|
60
|
-
- adds a `getMetaData()` method
|
|
61
|
-
- adds a `getThumbnail()` method
|
|
62
|
-
|
|
63
|
-
## GitFolder
|
|
64
|
-
Extends [Folder](#folder)
|
|
65
|
-
- the `constructor()` takes the git owner, repo, and baseUrl (defaults to "https://github.com")
|
|
66
|
-
- adds a [git](https://git-scm.com/) property, provided by [simple-git](https://www.npmjs.com/package/simple-git)
|
|
67
|
-
- adds a `clone()` method, using the owner, repo and baseUrl provided in the constructor
|
|
68
|
-
- adds a `pull()` method, that will clone the repo if it does not already exist
|
package/dist/index.d.ts
CHANGED
|
@@ -1,270 +1,503 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import { AvifOptions,
|
|
10
|
-
import * as
|
|
11
|
-
import
|
|
1
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
2
|
+
import { inspect } from "node:util";
|
|
3
|
+
import * as fluent_command0$1 from "fluent-command";
|
|
4
|
+
import * as fluent_command0 from "fluent-command";
|
|
5
|
+
import { WriteFileOptions } from "fs-extra/esm";
|
|
6
|
+
import * as neverthrow0$1 from "neverthrow";
|
|
7
|
+
import * as neverthrow0 from "neverthrow";
|
|
8
|
+
import { ResultAsync } from "neverthrow";
|
|
9
|
+
import sharpLib, { AvifOptions, ResizeOptions, SharpOptions } from "sharp";
|
|
10
|
+
import * as zerde0 from "zerde";
|
|
11
|
+
import * as zerde1 from "zerde";
|
|
12
|
+
import { ParseOptions, Prettify, StringifyOptions } from "zerde";
|
|
13
|
+
import * as zod0 from "zod";
|
|
14
|
+
import z from "zod";
|
|
15
|
+
import * as fs0$1 from "fs";
|
|
16
|
+
import * as fs0 from "fs";
|
|
17
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
//#region src/common/glob.d.ts
|
|
20
|
+
type FindFoldersGlobOptions = {
|
|
21
|
+
recursive?: boolean;
|
|
22
|
+
glob?: string;
|
|
23
|
+
};
|
|
24
|
+
type FindFilesGlobOptions = FindFoldersGlobOptions & {
|
|
25
|
+
exts?: Array<string>;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/common/node.d.ts
|
|
29
|
+
declare const FsFlags: {
|
|
30
|
+
readonly Append: "a";
|
|
31
|
+
readonly AppendOrFail: "ax";
|
|
32
|
+
readonly AppendAndRead: "a+";
|
|
33
|
+
readonly AppendAndReadOrFail: "ax+";
|
|
34
|
+
readonly Read: "r";
|
|
35
|
+
readonly ReadAndWrite: "r+";
|
|
36
|
+
readonly Write: "w";
|
|
37
|
+
readonly WriteOrFailIfExists: "wx";
|
|
38
|
+
};
|
|
39
|
+
type FsFlag = (typeof FsFlags)[keyof typeof FsFlags];
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/file/file.errors.d.ts
|
|
42
|
+
type FileOperation = "Append" | "Chmod" | "Copy" | "Download" | "ImageConvert" | "ImageMetadata" | "ImagePhash" | "ImageResize" | "Link" | "Move" | "Read" | "Remove" | "Stat" | "SymLink" | "Write";
|
|
43
|
+
declare abstract class FileError extends Error {
|
|
44
|
+
readonly operation: FileOperation;
|
|
45
|
+
readonly path: string;
|
|
46
|
+
constructor(path: string, cause: unknown);
|
|
36
47
|
}
|
|
37
|
-
declare class
|
|
38
|
-
|
|
48
|
+
declare class FileDownloadError extends FileError {
|
|
49
|
+
readonly url: string;
|
|
50
|
+
constructor(path: string, url: string, cause: unknown);
|
|
39
51
|
}
|
|
40
|
-
declare class
|
|
41
|
-
|
|
52
|
+
declare class FileAppendError extends FileError {}
|
|
53
|
+
declare class FileChmodError extends FileError {}
|
|
54
|
+
declare class FileImageConvertError extends FileError {}
|
|
55
|
+
declare class FileImageMetadataError extends FileError {}
|
|
56
|
+
declare class FileImagePhashError extends FileError {}
|
|
57
|
+
declare class FileImageResizeError extends FileError {}
|
|
58
|
+
declare class FileReadError extends FileError {}
|
|
59
|
+
declare class FileRemoveError extends FileError {}
|
|
60
|
+
declare class FileStatError extends FileError {}
|
|
61
|
+
declare class FileWriteError extends FileError {}
|
|
62
|
+
declare const DESTINATION_FALLBACK = "DESTINATION_FALLBACK";
|
|
63
|
+
declare abstract class FileRelocateError extends FileError {
|
|
64
|
+
readonly destinationPath: string;
|
|
65
|
+
constructor(path: string, destinationPath: string | null, cause: unknown);
|
|
42
66
|
}
|
|
43
|
-
declare class
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
declare class FileCopyError extends FileRelocateError {}
|
|
68
|
+
declare class FileMoveError extends FileRelocateError {}
|
|
69
|
+
declare class FileLinkError extends FileRelocateError {}
|
|
70
|
+
declare class FileSymLinkError extends FileRelocateError {}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/folder/folder.errors.d.ts
|
|
73
|
+
declare class FolderError extends Error {
|
|
74
|
+
readonly operation: string;
|
|
75
|
+
readonly path: string;
|
|
76
|
+
constructor(operation: string, path: string, cause?: unknown);
|
|
46
77
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
constructor(path: string, cause: Error);
|
|
78
|
+
declare class FolderGlobError extends FolderError {
|
|
79
|
+
constructor(path: string, cause?: unknown);
|
|
50
80
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/folder/folder.d.ts
|
|
83
|
+
declare class FluentFolder {
|
|
84
|
+
readonly path: string;
|
|
85
|
+
readonly name: string;
|
|
86
|
+
constructor(...pathPieces: PathPieces);
|
|
87
|
+
get info(): {
|
|
88
|
+
path: string;
|
|
89
|
+
name: string;
|
|
90
|
+
};
|
|
91
|
+
folder: (...pathPieces: PathPieces) => FluentFolder;
|
|
92
|
+
file: (file: string, ...extraPathPieces: PathPieces) => FluentFile<string, string>;
|
|
93
|
+
relativePath: (relativeTo?: FluentFolder) => string;
|
|
94
|
+
toString: () => string;
|
|
95
|
+
toJSON: () => {
|
|
96
|
+
FluentFolder: {
|
|
97
|
+
path: string;
|
|
98
|
+
name: string;
|
|
68
99
|
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
name: string;
|
|
75
|
-
parentName: string;
|
|
76
|
-
parentPath: string;
|
|
77
|
-
};
|
|
100
|
+
};
|
|
101
|
+
[inspect.custom]: () => {
|
|
102
|
+
FluentFolder: {
|
|
103
|
+
path: string;
|
|
104
|
+
name: string;
|
|
78
105
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
getStats: () => neverthrow.ResultAsync<fs.Stats, NodeError | FolderWasNotFolderError>;
|
|
88
|
-
exists: () => Promise<boolean>;
|
|
89
|
-
ensureExists: () => Promise<void>;
|
|
90
|
-
ensureEmpty: () => Promise<void>;
|
|
91
|
-
delete: () => Promise<void>;
|
|
92
|
-
getParentFolder: () => Folder;
|
|
93
|
-
subFolder: (folder: string | Folder, ...extraPathPieces: Strings) => Folder;
|
|
94
|
-
childFolders: (globPattern?: AnyGlob) => neverthrow.ResultAsync<Folder[], NodeError | FolderWasNotFolderError>;
|
|
95
|
-
findFolders: (globPattern: AnyGlob) => neverthrow.ResultAsync<Folder[], NodeError | FolderWasNotFolderError>;
|
|
96
|
-
}
|
|
97
|
-
declare function folder(folder?: string | Folder): Folder;
|
|
98
|
-
declare function folder(folder: string | Folder, ...extraPathPieces: Strings): Folder;
|
|
99
|
-
declare function homeFolder(...extraPathPieces: Strings): Folder;
|
|
100
|
-
declare function cwd(): string;
|
|
101
|
-
|
|
102
|
-
type AnyGlob = string | Strings | GlobOptions;
|
|
103
|
-
type GlobOptions = Omit<Options, "cwd"> & Partial<{
|
|
104
|
-
patterns: Strings;
|
|
105
|
-
extensions: Strings;
|
|
106
|
-
}>;
|
|
107
|
-
|
|
108
|
-
declare class AFile {
|
|
109
|
-
#private;
|
|
110
|
-
constructor(file: AFile | Folder | string, ...extraPathPieces: Strings);
|
|
111
|
-
get path(): string;
|
|
112
|
-
get fullName(): string;
|
|
113
|
-
get name(): string;
|
|
114
|
-
get ext(): string;
|
|
115
|
-
get parentName(): string;
|
|
116
|
-
get parentPath(): string;
|
|
117
|
-
get info(): {
|
|
118
|
-
path: string;
|
|
119
|
-
fullName: string;
|
|
120
|
-
name: string;
|
|
121
|
-
ext: string;
|
|
122
|
-
parentName: string;
|
|
123
|
-
parentPath: string;
|
|
124
|
-
};
|
|
125
|
-
relativePath: (relativeTo?: string) => string;
|
|
126
|
-
toString: () => string;
|
|
127
|
-
toJSON: () => {
|
|
128
|
-
File: {
|
|
129
|
-
path: string;
|
|
130
|
-
fullName: string;
|
|
131
|
-
name: string;
|
|
132
|
-
ext: string;
|
|
133
|
-
parentName: string;
|
|
134
|
-
parentPath: string;
|
|
135
|
-
};
|
|
136
|
-
};
|
|
137
|
-
[inspect.custom]: () => {
|
|
138
|
-
File: {
|
|
139
|
-
path: string;
|
|
140
|
-
fullName: string;
|
|
141
|
-
name: string;
|
|
142
|
-
ext: string;
|
|
143
|
-
parentName: string;
|
|
144
|
-
parentPath: string;
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
getParentFolder: () => Folder;
|
|
148
|
-
getStats: () => neverthrow.ResultAsync<fs.Stats, NodeError | FileWasNotFileError>;
|
|
149
|
-
exists: () => Promise<boolean>;
|
|
150
|
-
ensureExists: () => Promise<void>;
|
|
151
|
-
copyTo: (destination: AFile | Folder) => Promise<void>;
|
|
152
|
-
moveTo: (destination: AFile | Folder) => Promise<void>;
|
|
153
|
-
linkTo: (destination: AFile | Folder) => Promise<void>;
|
|
154
|
-
symlinkTo: (destination: AFile | Folder) => Promise<void>;
|
|
155
|
-
delete: () => Promise<void>;
|
|
156
|
-
readText: () => neverthrow.ResultAsync<string, FileNotFoundError | FileReadError | FileWasNotFileError>;
|
|
157
|
-
readTextLines: () => neverthrow.ResultAsync<string[], FileNotFoundError | FileReadError | FileWasNotFileError>;
|
|
158
|
-
writeText: (content: string) => neverthrow.ResultAsync<void, FileWriteError | FileWasNotFileError>;
|
|
159
|
-
}
|
|
160
|
-
declare function afile(file: AFile | Folder | string, ...extraPathPieces: Strings): AFile;
|
|
161
|
-
declare function homeFile(file: AFile | Folder | string, ...extraPathPieces: Strings): AFile;
|
|
162
|
-
declare function findFiles(inFolder?: Folder, anyGlob?: AnyGlob): neverthrow.ResultAsync<AFile[], NodeError | FolderWasNotFolderError>;
|
|
163
|
-
|
|
164
|
-
declare class ZodParseError extends Error {
|
|
165
|
-
nestedErrors: UnknownObject;
|
|
166
|
-
constructor({ cause, nestedErrors, }: {
|
|
167
|
-
cause: ZodError;
|
|
168
|
-
nestedErrors: UnknownObject;
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
declare class JsonFile<FileSchema extends ZodTypeAny> extends AFile {
|
|
173
|
-
protected readonly fileSchema: FileSchema;
|
|
174
|
-
constructor(fileSchema: FileSchema, filePath: AFile | Folder | string, ...extraPathPieces: Strings);
|
|
175
|
-
protected validateUnknown: (unknownContents: unknown) => neverthrow.Result<z.TypeOf<FileSchema>, ZodParseError>;
|
|
176
|
-
read: () => neverthrow.ResultAsync<z.TypeOf<FileSchema>, FileNotFoundError | FileReadError | FileWasNotFileError | ZodParseError | JSONError>;
|
|
177
|
-
write: (contents: z.infer<FileSchema>, spacing?: Spacing) => neverthrow.ResultAsync<void, Error | FileWriteError | FileWasNotFileError | ZodParseError>;
|
|
178
|
-
}
|
|
179
|
-
declare function jsonFile<FileSchema extends ZodTypeAny>(fileSchema: FileSchema, filePath: AFile | Folder | string, ...extraPathPieces: Strings): JsonFile<FileSchema>;
|
|
180
|
-
declare function findJsonFiles<FileSchema extends ZodTypeAny>(fileSchema: FileSchema, inFolder?: Folder, anyGlob?: AnyGlob): neverthrow.ResultAsync<JsonFile<FileSchema>[], NodeError | FolderWasNotFolderError>;
|
|
181
|
-
|
|
182
|
-
type YamlParseOptions = ParseOptions & DocumentOptions & SchemaOptions & ToJSOptions;
|
|
183
|
-
type YamlStringifyOptions = (DocumentOptions & SchemaOptions & CreateNodeOptions & ToStringOptions) | Spacing;
|
|
184
|
-
|
|
185
|
-
declare const YAML_EXTENSIONS: string[];
|
|
186
|
-
declare class YamlFile<FileSchema extends ZodTypeAny> extends JsonFile<FileSchema> {
|
|
187
|
-
read: (parseOptions?: YamlParseOptions) => neverthrow.ResultAsync<z.TypeOf<FileSchema>, FileNotFoundError | FileReadError | FileWasNotFileError | ZodParseError | YAMLParseError>;
|
|
188
|
-
write: (contents: z.infer<FileSchema>, stringifyOptions?: YamlStringifyOptions) => neverthrow.ResultAsync<void, FileWriteError | FileWasNotFileError | StringifyError | ZodParseError>;
|
|
189
|
-
}
|
|
190
|
-
declare function yamlFile<FileSchema extends ZodTypeAny>(fileSchema: FileSchema, filePath: AFile | Folder | string, ...extraPathPieces: Strings): YamlFile<FileSchema>;
|
|
191
|
-
declare function findYamlFiles<FileSchema extends ZodTypeAny>(fileSchema: FileSchema, inFolder?: Folder, anyGlob?: AnyGlob): neverthrow.ResultAsync<YamlFile<FileSchema>[], NodeError | FolderWasNotFolderError>;
|
|
192
|
-
|
|
193
|
-
declare const IMAGE_EXTENSIONS: string[];
|
|
194
|
-
type ToAvifOptions = {
|
|
195
|
-
newFolder?: Folder;
|
|
196
|
-
newName?: string;
|
|
197
|
-
height?: number;
|
|
198
|
-
width?: number;
|
|
199
|
-
} & Pick<AvifOptions, "effort" | "quality">;
|
|
200
|
-
declare class ImageFile extends AFile {
|
|
201
|
-
readonly sharp: Sharp;
|
|
202
|
-
constructor(file: AFile | Folder | string, ...extraPathPieces: Strings);
|
|
203
|
-
convertToAvif: ({ newFolder, newName, height, width, effort, quality, }?: ToAvifOptions) => Promise<ImageFile>;
|
|
204
|
-
getPhash: () => Promise<string>;
|
|
106
|
+
};
|
|
107
|
+
stats: () => ResultAsync<fs0$1.Stats, FolderError>;
|
|
108
|
+
exists: () => Promise<boolean>;
|
|
109
|
+
ensureExists: () => Promise<void>;
|
|
110
|
+
ensureEmpty: () => Promise<void>;
|
|
111
|
+
remove: () => Promise<void>;
|
|
112
|
+
findFolders: (findFoldersOptions?: FindFoldersGlobOptions | undefined) => ResultAsync<FluentFolder[], FolderGlobError>;
|
|
113
|
+
findFiles: (findFilesOptions?: FindFilesGlobOptions | undefined) => ResultAsync<FluentFile<string, string>[], FolderGlobError>;
|
|
205
114
|
}
|
|
206
|
-
declare function
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
115
|
+
declare function folder(...pathPieces: PathPieces): FluentFolder;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/common/path.d.ts
|
|
118
|
+
type PathPieces = ReadonlyArray<string | number>;
|
|
119
|
+
type FileOutputOptions = {
|
|
120
|
+
newFolder?: FluentFolder;
|
|
121
|
+
newBaseName?: string;
|
|
213
122
|
};
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
type
|
|
220
|
-
|
|
221
|
-
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/file/image.d.ts
|
|
125
|
+
declare const MAX_DIFFERENCES = 6;
|
|
126
|
+
declare const IMAGE_EXTENSIONS: string[];
|
|
127
|
+
type ImageResizeOptions = FileOutputOptions & ResizeOptions;
|
|
128
|
+
type ToAVIFOptions = FileOutputOptions & ResizeOptions & AvifOptions;
|
|
129
|
+
type PhashSimilarity = "SAME" | "SIMILAR" | "DIFFERENT";
|
|
130
|
+
type PhashSimilarityInfo = {
|
|
131
|
+
readonly level: PhashSimilarity;
|
|
132
|
+
readonly diff: number;
|
|
222
133
|
};
|
|
134
|
+
declare const binaryStringRegex: RegExp;
|
|
135
|
+
declare const phashStringSchema: z.ZodCodec<z.ZodString, z.core.$ZodBranded<z.ZodCustomStringFormat<"hex">, "PhashString">>;
|
|
136
|
+
type PhashString = z.output<typeof phashStringSchema>;
|
|
137
|
+
declare const phashThresholdSchema: z.core.$ZodBranded<z.ZodInt, "PhashThreshold">;
|
|
138
|
+
type PhashThreshold = z.infer<typeof phashThresholdSchema>;
|
|
139
|
+
declare const DEFAULT_PHASH_THRESHOLD: number & z.core.$brand<"PhashThreshold">;
|
|
140
|
+
/**
|
|
141
|
+
* Compares two phashes, and returns information about how similar the two phashes are, based on the (optional) threshold.
|
|
142
|
+
*
|
|
143
|
+
* @param {PhashString} phashA
|
|
144
|
+
* @param {PhashString} phashB
|
|
145
|
+
* @param {PhashThreshold} [threshold] - Must be an integer 0-64 (inclusive). Defaults to 6.
|
|
146
|
+
* If there are fewer than `threshold` differences, the images are similar
|
|
147
|
+
*
|
|
148
|
+
* @example Given a needle and a haystack:
|
|
149
|
+
```ts
|
|
150
|
+
const phashNeedle = "1a...8f" // (hex-encoded string)
|
|
151
|
+
const phashHaystack = ["2b..3c", "4d..5e"] // (Array of hex-encoded strings)
|
|
152
|
+
```
|
|
153
|
+
*
|
|
154
|
+
* Can be used for sorting by using the `diff` count of the return value
|
|
155
|
+
* @example
|
|
156
|
+
```ts
|
|
157
|
+
const sorted = phashHaystack.sort((phashA, phashB) => {
|
|
158
|
+
const adiff = comparePhashes(phashNeedle, phashA).diff
|
|
159
|
+
const bdiff = comparePhashes(phashNeedle, phashB).diff
|
|
160
|
+
return adiff - bdiff
|
|
161
|
+
})
|
|
162
|
+
```
|
|
223
163
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
164
|
+
* Can be used for filtering by checking the `level` of the return value
|
|
165
|
+
* @example
|
|
166
|
+
```ts
|
|
167
|
+
// all items in haystack that are the same or similar to the needle
|
|
168
|
+
const matches = phashHaystack.filter(hay => comparePhashes(phashNeedle, hay).level !== "DIFFERENT")
|
|
169
|
+
```
|
|
170
|
+
*
|
|
171
|
+
* @see `phashesMatch` For a simpler function that returns a boolean of whether the two phashes are similar
|
|
172
|
+
*/
|
|
173
|
+
declare function comparePhashes(phashA: PhashString, phashB: PhashString, threshold?: PhashThreshold): PhashSimilarityInfo;
|
|
174
|
+
/**
|
|
175
|
+
* Checks if two phashes `match` based on the (optional) threshold
|
|
176
|
+
*
|
|
177
|
+
* @param {PhashString} phashA
|
|
178
|
+
* @param {PhashString} phashB
|
|
179
|
+
* @param {PhashThreshold} [threshold] - Must be an integer 0-64 (inclusive). Defaults to 6.
|
|
180
|
+
* If there are fewer than `threshold` differences, the images are similar
|
|
181
|
+
*
|
|
182
|
+
* @returns whether the two phashes are similar
|
|
183
|
+
*/
|
|
184
|
+
declare function phashesMatch(phashA: PhashString, phashB: PhashString, threshold?: PhashThreshold): boolean;
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/file/video.d.ts
|
|
231
187
|
declare const VIDEO_EXTENSIONS: string[];
|
|
232
|
-
declare
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
188
|
+
declare function getVideoMetaData(path: string): neverthrow0$1.ResultAsync<{
|
|
189
|
+
millis: number;
|
|
190
|
+
seconds: number;
|
|
191
|
+
minutes: number;
|
|
192
|
+
hours: number;
|
|
193
|
+
hms: string;
|
|
194
|
+
nb_streams: number;
|
|
195
|
+
format_name: string;
|
|
196
|
+
format_long_name: string;
|
|
197
|
+
size: number;
|
|
198
|
+
bit_rate: number;
|
|
199
|
+
videoStream: {
|
|
200
|
+
millis: number;
|
|
201
|
+
seconds: number;
|
|
202
|
+
minutes: number;
|
|
203
|
+
hours: number;
|
|
204
|
+
hms: string;
|
|
205
|
+
codec_type: "video";
|
|
206
|
+
height: number;
|
|
207
|
+
width: number;
|
|
208
|
+
codec_name: string;
|
|
209
|
+
codec_long_name: string;
|
|
210
|
+
codec_tag_string: string;
|
|
211
|
+
nb_frames: number;
|
|
212
|
+
bit_rate: number;
|
|
213
|
+
};
|
|
214
|
+
audioStreams: {
|
|
215
|
+
millis: number;
|
|
216
|
+
seconds: number;
|
|
217
|
+
minutes: number;
|
|
218
|
+
hours: number;
|
|
219
|
+
hms: string;
|
|
220
|
+
codec_type: "audio";
|
|
221
|
+
channels: number;
|
|
222
|
+
channel_layout: string;
|
|
223
|
+
codec_name: string;
|
|
224
|
+
codec_long_name: string;
|
|
225
|
+
codec_tag_string: string;
|
|
226
|
+
nb_frames: number;
|
|
227
|
+
bit_rate: number;
|
|
228
|
+
}[];
|
|
229
|
+
}, fluent_command0$1.FluentCommandError | zerde0.ParseError | zerde0.ValidationError>;
|
|
230
|
+
type ExtractFrameOptions = FileOutputOptions & {
|
|
231
|
+
time?: number;
|
|
232
|
+
unit?: "s" | "ms" | "us";
|
|
233
|
+
ext?: "png" | "jpg";
|
|
234
|
+
};
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region src/file/download.d.ts
|
|
237
|
+
type DownloadProgress = {
|
|
238
|
+
totalBytes: number;
|
|
239
|
+
downloadedBytes: number;
|
|
240
|
+
};
|
|
241
|
+
type DownloadFileOptions = Prettify<Partial<{
|
|
242
|
+
overwrite: boolean;
|
|
243
|
+
progressThrottle: number;
|
|
244
|
+
onProgress: (progressStats: DownloadProgress) => void;
|
|
245
|
+
}>>;
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/file/file.d.ts
|
|
248
|
+
declare class FluentFile<Content = string, ParsedContent = Content> {
|
|
249
|
+
#private;
|
|
250
|
+
constructor(schema: StandardSchemaV1<Content, ParsedContent>, file: string | number, ...pathPieces: PathPieces);
|
|
251
|
+
toString: () => string;
|
|
252
|
+
toJSON: () => {
|
|
253
|
+
FluentFile: {
|
|
254
|
+
absolutePath: string;
|
|
255
|
+
folderPath: string;
|
|
256
|
+
name: string;
|
|
257
|
+
basename: string;
|
|
258
|
+
ext: string;
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
[inspect.custom]: () => {
|
|
262
|
+
FluentFile: {
|
|
263
|
+
absolutePath: string;
|
|
264
|
+
folderPath: string;
|
|
265
|
+
name: string;
|
|
266
|
+
basename: string;
|
|
267
|
+
ext: string;
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
path: () => string;
|
|
271
|
+
folderPath: () => string;
|
|
272
|
+
name(): string;
|
|
273
|
+
name(newName: string): this;
|
|
274
|
+
basename(): string;
|
|
275
|
+
basename(newBaseName: string): this;
|
|
276
|
+
ext(): string;
|
|
277
|
+
ext(newExtension: string): this;
|
|
278
|
+
schema: <NewSchema extends StandardSchemaV1>(newSchema: NewSchema) => FluentFile<StandardSchemaV1.InferInput<NewSchema>, StandardSchemaV1.InferOutput<NewSchema>>;
|
|
279
|
+
get info(): {
|
|
280
|
+
absolutePath: string;
|
|
281
|
+
folderPath: string;
|
|
282
|
+
name: string;
|
|
283
|
+
basename: string;
|
|
284
|
+
ext: string;
|
|
285
|
+
};
|
|
286
|
+
relativePath: (relativeTo?: FluentFolder) => string;
|
|
287
|
+
file: (file: string, ...extraPathPieces: PathPieces) => FluentFile<Content, ParsedContent>;
|
|
288
|
+
folder: (...extraPathPieces: PathPieces) => FluentFolder;
|
|
289
|
+
stats: () => ResultAsync<fs0.Stats, FileStatError>;
|
|
290
|
+
exists: () => Promise<boolean>;
|
|
291
|
+
ensureExists: () => Promise<void>;
|
|
292
|
+
chmod: (mode: string | number) => ResultAsync<void, FileChmodError>;
|
|
293
|
+
copyTo: (destination: FluentFolder | FluentFile<string, string>) => ResultAsync<void, FileCopyError>;
|
|
294
|
+
moveTo: (destination: FluentFolder | FluentFile<string, string>, dereferenceSymlinks?: boolean | undefined) => ResultAsync<void, FileMoveError>;
|
|
295
|
+
linkTo: (destination: FluentFolder | FluentFile<string, string>) => ResultAsync<void, FileLinkError>;
|
|
296
|
+
symlinkTo: (destination: FluentFolder | FluentFile<string, string>) => ResultAsync<void, FileSymLinkError>;
|
|
297
|
+
remove: () => ResultAsync<void, FileRemoveError>;
|
|
298
|
+
readText: (options?: FileReadOptions | undefined) => ResultAsync<string, FileReadError>;
|
|
299
|
+
readBuffer: (options?: Pick<FileReadOptions, "flag" | "signal"> | undefined) => ResultAsync<Buffer<ArrayBufferLike>, FileReadError>;
|
|
300
|
+
read: (options?: FileReadOptions) => ResultAsync<ParsedContent, FileReadError | zerde1.ParseError | zerde1.ValidationError>;
|
|
301
|
+
createReadStream: (readStreamOptions?: Parameters<typeof createReadStream>[1]) => fs0.ReadStream;
|
|
302
|
+
writeText: (textContent: string, writeOptions?: WriteFileOptions | undefined) => ResultAsync<void, FileWriteError>;
|
|
303
|
+
writeBuffer: (bufferContent: Buffer<ArrayBufferLike>, writeOptions?: WriteFileOptions | undefined) => ResultAsync<void, FileWriteError>;
|
|
304
|
+
write: (content: Content, options?: FileWriteOptions) => ResultAsync<void, FileWriteError | zerde1.ValidationError | zerde1.StringifyError>;
|
|
305
|
+
createWriteStream: (writeStreamOptions?: Parameters<typeof createWriteStream>[1]) => fs0.WriteStream;
|
|
306
|
+
append: (data: string | Buffer<ArrayBufferLike>, writeOptions?: WriteFileOptions | undefined) => ResultAsync<void, FileAppendError>;
|
|
307
|
+
image: (sharpOptions?: SharpOptions) => {
|
|
308
|
+
metadata: () => ResultAsync<sharpLib.Metadata, FileImageMetadataError>;
|
|
309
|
+
resize: (resizeOptions: ImageResizeOptions) => ResultAsync<FluentFile<string, string>, FileImageResizeError>;
|
|
310
|
+
toAVIF: (toAVIFOptions?: ToAVIFOptions | undefined) => ResultAsync<FluentFile<string, string>, FileImageConvertError>;
|
|
311
|
+
/**
|
|
312
|
+
* Calculate the perceptual hash of an image
|
|
313
|
+
* @returns A hex-encoded string
|
|
314
|
+
* @see Use `comparePhashes` or `phashesMatch` to compare the returned phash
|
|
315
|
+
*/
|
|
316
|
+
phash: () => ResultAsync<string & zod0.$brand<"PhashString">, FileImagePhashError>;
|
|
317
|
+
removeAlpha(): sharpLib.Sharp;
|
|
318
|
+
ensureAlpha(alpha?: number): sharpLib.Sharp;
|
|
319
|
+
extractChannel(channel: 0 | 1 | 2 | 3 | "red" | "green" | "blue" | "alpha"): sharpLib.Sharp;
|
|
320
|
+
joinChannel(images: string | Buffer | ArrayLike<string | Buffer>, options?: SharpOptions): sharpLib.Sharp;
|
|
321
|
+
bandbool(boolOp: keyof sharpLib.BoolEnum): sharpLib.Sharp;
|
|
322
|
+
tint(tint: sharpLib.Colour | sharpLib.Color): sharpLib.Sharp;
|
|
323
|
+
greyscale(greyscale?: boolean): sharpLib.Sharp;
|
|
324
|
+
grayscale(grayscale?: boolean): sharpLib.Sharp;
|
|
325
|
+
pipelineColourspace(colourspace?: string): sharpLib.Sharp;
|
|
326
|
+
pipelineColorspace(colorspace?: string): sharpLib.Sharp;
|
|
327
|
+
toColourspace(colourspace?: string): sharpLib.Sharp;
|
|
328
|
+
toColorspace(colorspace: string): sharpLib.Sharp;
|
|
329
|
+
composite(images: sharpLib.OverlayOptions[]): sharpLib.Sharp;
|
|
330
|
+
clone(): sharpLib.Sharp;
|
|
331
|
+
keepMetadata(): sharpLib.Sharp;
|
|
332
|
+
stats(callback: (err: Error, stats: sharpLib.Stats) => void): sharpLib.Sharp;
|
|
333
|
+
stats(): Promise<sharpLib.Stats>;
|
|
334
|
+
rotate(angle?: number, options?: sharpLib.RotateOptions): sharpLib.Sharp;
|
|
335
|
+
autoOrient(): sharpLib.Sharp;
|
|
336
|
+
flip(flip?: boolean): sharpLib.Sharp;
|
|
337
|
+
flop(flop?: boolean): sharpLib.Sharp;
|
|
338
|
+
affine(matrix: [number, number, number, number] | sharpLib.Matrix2x2, options?: sharpLib.AffineOptions): sharpLib.Sharp;
|
|
339
|
+
sharpen(options?: sharpLib.SharpenOptions): sharpLib.Sharp;
|
|
340
|
+
sharpen(sigma?: number, flat?: number, jagged?: number): sharpLib.Sharp;
|
|
341
|
+
median(size?: number): sharpLib.Sharp;
|
|
342
|
+
blur(sigma?: number | boolean | sharpLib.BlurOptions): sharpLib.Sharp;
|
|
343
|
+
dilate(width?: number): sharpLib.Sharp;
|
|
344
|
+
erode(width?: number): sharpLib.Sharp;
|
|
345
|
+
flatten(flatten?: boolean | sharpLib.FlattenOptions): sharpLib.Sharp;
|
|
346
|
+
unflatten(): sharpLib.Sharp;
|
|
347
|
+
gamma(gamma?: number, gammaOut?: number): sharpLib.Sharp;
|
|
348
|
+
negate(negate?: boolean | sharpLib.NegateOptions): sharpLib.Sharp;
|
|
349
|
+
normalise(normalise?: sharpLib.NormaliseOptions): sharpLib.Sharp;
|
|
350
|
+
normalize(normalize?: sharpLib.NormaliseOptions): sharpLib.Sharp;
|
|
351
|
+
clahe(options: sharpLib.ClaheOptions): sharpLib.Sharp;
|
|
352
|
+
convolve(kernel: sharpLib.Kernel): sharpLib.Sharp;
|
|
353
|
+
threshold(threshold?: number, options?: sharpLib.ThresholdOptions): sharpLib.Sharp;
|
|
354
|
+
boolean(operand: string | Buffer, operator: keyof sharpLib.BoolEnum, options?: {
|
|
355
|
+
raw: sharpLib.Raw;
|
|
356
|
+
}): sharpLib.Sharp;
|
|
357
|
+
linear(a?: number | number[] | null, b?: number | number[]): sharpLib.Sharp;
|
|
358
|
+
recomb(inputMatrix: sharpLib.Matrix3x3 | sharpLib.Matrix4x4): sharpLib.Sharp;
|
|
359
|
+
modulate(options?: {
|
|
360
|
+
brightness?: number | undefined;
|
|
361
|
+
saturation?: number | undefined;
|
|
362
|
+
hue?: number | undefined;
|
|
363
|
+
lightness?: number | undefined;
|
|
364
|
+
}): sharpLib.Sharp;
|
|
365
|
+
toFile(fileOut: string, callback: (err: Error, info: sharpLib.OutputInfo) => void): sharpLib.Sharp;
|
|
366
|
+
toFile(fileOut: string): Promise<sharpLib.OutputInfo>;
|
|
367
|
+
toBuffer(callback: (err: Error, buffer: Buffer, info: sharpLib.OutputInfo) => void): sharpLib.Sharp;
|
|
368
|
+
toBuffer(options?: {
|
|
369
|
+
resolveWithObject: false;
|
|
370
|
+
}): Promise<Buffer>;
|
|
371
|
+
toBuffer(options: {
|
|
372
|
+
resolveWithObject: true;
|
|
373
|
+
}): Promise<{
|
|
374
|
+
data: Buffer;
|
|
375
|
+
info: sharpLib.OutputInfo;
|
|
376
|
+
}>;
|
|
377
|
+
keepExif(): sharpLib.Sharp;
|
|
378
|
+
withExif(exif: sharpLib.Exif): sharpLib.Sharp;
|
|
379
|
+
withExifMerge(exif: sharpLib.Exif): sharpLib.Sharp;
|
|
380
|
+
keepIccProfile(): sharpLib.Sharp;
|
|
381
|
+
withIccProfile(icc: string, options?: sharpLib.WithIccProfileOptions): sharpLib.Sharp;
|
|
382
|
+
keepXmp(): sharpLib.Sharp;
|
|
383
|
+
withXmp(xmp: string): sharpLib.Sharp;
|
|
384
|
+
withMetadata(withMetadata?: sharpLib.WriteableMetadata): sharpLib.Sharp;
|
|
385
|
+
jpeg(options?: sharpLib.JpegOptions): sharpLib.Sharp;
|
|
386
|
+
jp2(options?: sharpLib.Jp2Options): sharpLib.Sharp;
|
|
387
|
+
jxl(options?: sharpLib.JxlOptions): sharpLib.Sharp;
|
|
388
|
+
png(options?: sharpLib.PngOptions): sharpLib.Sharp;
|
|
389
|
+
webp(options?: sharpLib.WebpOptions): sharpLib.Sharp;
|
|
390
|
+
gif(options?: sharpLib.GifOptions): sharpLib.Sharp;
|
|
391
|
+
avif(options?: sharpLib.AvifOptions): sharpLib.Sharp;
|
|
392
|
+
heif(options?: sharpLib.HeifOptions): sharpLib.Sharp;
|
|
393
|
+
tiff(options?: sharpLib.TiffOptions): sharpLib.Sharp;
|
|
394
|
+
raw(options?: sharpLib.RawOptions): sharpLib.Sharp;
|
|
395
|
+
toFormat(format: keyof sharpLib.FormatEnum | sharpLib.AvailableFormatInfo, options?: sharpLib.OutputOptions | sharpLib.JpegOptions | sharpLib.PngOptions | sharpLib.WebpOptions | sharpLib.AvifOptions | sharpLib.HeifOptions | sharpLib.JxlOptions | sharpLib.GifOptions | sharpLib.Jp2Options | sharpLib.RawOptions | sharpLib.TiffOptions): sharpLib.Sharp;
|
|
396
|
+
tile(tile?: sharpLib.TileOptions): sharpLib.Sharp;
|
|
397
|
+
timeout(options: sharpLib.TimeoutOptions): sharpLib.Sharp;
|
|
398
|
+
extend(extend: number | sharpLib.ExtendOptions): sharpLib.Sharp;
|
|
399
|
+
extract(region: sharpLib.Region): sharpLib.Sharp;
|
|
400
|
+
trim(options?: sharpLib.TrimOptions): sharpLib.Sharp;
|
|
401
|
+
allowHalfOpen: boolean;
|
|
402
|
+
off<K>(eventName: string | symbol, listener: (...args: any[]) => void): sharpLib.Sharp;
|
|
403
|
+
removeAllListeners(eventName?: string | symbol | undefined): sharpLib.Sharp;
|
|
404
|
+
setMaxListeners(n: number): sharpLib.Sharp;
|
|
405
|
+
getMaxListeners(): number;
|
|
406
|
+
listeners<K>(eventName: string | symbol): Function[];
|
|
407
|
+
rawListeners<K>(eventName: string | symbol): Function[];
|
|
408
|
+
listenerCount<K>(eventName: string | symbol, listener?: Function | undefined): number;
|
|
409
|
+
eventNames(): (string | symbol)[];
|
|
410
|
+
readableAborted: boolean;
|
|
411
|
+
readable: boolean;
|
|
412
|
+
readableDidRead: boolean;
|
|
413
|
+
readableEncoding: BufferEncoding | null;
|
|
414
|
+
readableEnded: boolean;
|
|
415
|
+
readableFlowing: boolean | null;
|
|
416
|
+
readableHighWaterMark: number;
|
|
417
|
+
readableLength: number;
|
|
418
|
+
readableObjectMode: boolean;
|
|
419
|
+
destroyed: boolean;
|
|
420
|
+
closed: boolean;
|
|
421
|
+
errored: Error | null;
|
|
422
|
+
writable: boolean;
|
|
423
|
+
writableAborted: boolean;
|
|
424
|
+
writableEnded: boolean;
|
|
425
|
+
writableFinished: boolean;
|
|
426
|
+
writableHighWaterMark: number;
|
|
427
|
+
writableLength: number;
|
|
428
|
+
writableObjectMode: boolean;
|
|
429
|
+
writableCorked: number;
|
|
430
|
+
writableNeedDrain: boolean;
|
|
431
|
+
};
|
|
432
|
+
video: () => {
|
|
433
|
+
metadata: () => ResultAsync<{
|
|
434
|
+
millis: number;
|
|
435
|
+
seconds: number;
|
|
436
|
+
minutes: number;
|
|
437
|
+
hours: number;
|
|
438
|
+
hms: string;
|
|
439
|
+
nb_streams: number;
|
|
440
|
+
format_name: string;
|
|
441
|
+
format_long_name: string;
|
|
442
|
+
size: number;
|
|
443
|
+
bit_rate: number;
|
|
444
|
+
videoStream: {
|
|
238
445
|
millis: number;
|
|
239
|
-
minutes: number;
|
|
240
446
|
seconds: number;
|
|
447
|
+
minutes: number;
|
|
448
|
+
hours: number;
|
|
449
|
+
hms: string;
|
|
450
|
+
codec_type: "video";
|
|
451
|
+
height: number;
|
|
241
452
|
width: number;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
getReadMe: () => AFile;
|
|
453
|
+
codec_name: string;
|
|
454
|
+
codec_long_name: string;
|
|
455
|
+
codec_tag_string: string;
|
|
456
|
+
nb_frames: number;
|
|
457
|
+
bit_rate: number;
|
|
458
|
+
};
|
|
459
|
+
audioStreams: {
|
|
460
|
+
millis: number;
|
|
461
|
+
seconds: number;
|
|
462
|
+
minutes: number;
|
|
463
|
+
hours: number;
|
|
464
|
+
hms: string;
|
|
465
|
+
codec_type: "audio";
|
|
466
|
+
channels: number;
|
|
467
|
+
channel_layout: string;
|
|
468
|
+
codec_name: string;
|
|
469
|
+
codec_long_name: string;
|
|
470
|
+
codec_tag_string: string;
|
|
471
|
+
nb_frames: number;
|
|
472
|
+
bit_rate: number;
|
|
473
|
+
}[];
|
|
474
|
+
}, fluent_command0.FluentCommandError | zerde1.ParseError | zerde1.ValidationError>;
|
|
475
|
+
extractFrame: (extractFrameOptions?: ExtractFrameOptions) => Promise<neverthrow0.Result<FluentFile<string, string>, fluent_command0.FluentCommandError>>;
|
|
476
|
+
};
|
|
477
|
+
download: (url: string, downloadOptions?: DownloadFileOptions) => ResultAsync<"ALREADY_EXISTS" | "SUCCESS", FileDownloadError>;
|
|
268
478
|
}
|
|
269
|
-
|
|
270
|
-
|
|
479
|
+
type FileReadOptions = ParseOptions & Partial<{
|
|
480
|
+
encoding: BufferEncoding;
|
|
481
|
+
flag: FsFlag;
|
|
482
|
+
signal: AbortSignal;
|
|
483
|
+
}>;
|
|
484
|
+
type FileWriteOptions = StringifyOptions & Partial<{
|
|
485
|
+
encoding: BufferEncoding;
|
|
486
|
+
mode: number;
|
|
487
|
+
flag: FsFlag;
|
|
488
|
+
flush: boolean;
|
|
489
|
+
signal: AbortSignal;
|
|
490
|
+
}>;
|
|
491
|
+
declare function ffile(file: PathPieces[number], ...extraPathPieces: PathPieces): FluentFile<string, string>;
|
|
492
|
+
declare function homeFile(file: PathPieces[number], ...extraPathPieces: PathPieces): FluentFile<string, string>;
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/folder/xdg.d.ts
|
|
495
|
+
declare function cacheFolder(...pathPieces: PathPieces): FluentFolder;
|
|
496
|
+
declare function configFolder(...pathPieces: PathPieces): FluentFolder;
|
|
497
|
+
declare function dataFolder(...pathPieces: PathPieces): FluentFolder;
|
|
498
|
+
declare function logsFolder(...pathPieces: PathPieces): FluentFolder;
|
|
499
|
+
declare function homeFolder(...pathPieces: PathPieces): FluentFolder;
|
|
500
|
+
declare function tempFolder(...pathPieces: PathPieces): FluentFolder;
|
|
501
|
+
declare function currentFolder(): FluentFolder;
|
|
502
|
+
//#endregion
|
|
503
|
+
export { DEFAULT_PHASH_THRESHOLD, DESTINATION_FALLBACK, ExtractFrameOptions, FileAppendError, FileChmodError, FileCopyError, FileDownloadError, FileImageConvertError, FileImageMetadataError, FileImagePhashError, FileImageResizeError, FileLinkError, FileMoveError, FileOperation, FileReadError, FileReadOptions, FileRemoveError, FileStatError, FileSymLinkError, FileWriteError, FileWriteOptions, FindFilesGlobOptions, FindFoldersGlobOptions, FluentFile, FluentFolder, FsFlag, FsFlags, IMAGE_EXTENSIONS, ImageResizeOptions, MAX_DIFFERENCES, PhashSimilarity, PhashSimilarityInfo, PhashString, PhashThreshold, ToAVIFOptions, VIDEO_EXTENSIONS, binaryStringRegex, cacheFolder, comparePhashes, configFolder, currentFolder, dataFolder, ffile, folder, getVideoMetaData, homeFile, homeFolder, logsFolder, phashStringSchema, phashThresholdSchema, phashesMatch, tempFolder };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
|
-
import{constants as kt}from"node:fs";import{copyFile as Gt}from"node:fs/promises";import{homedir as $t}from"node:os";import{inspect as Ut}from"node:util";import{stat as V}from"node:fs/promises";var J=Object.keys;function F(e){if(!(e instanceof Error&&"code"in e&&typeof e.code=="string"))throw new Error("was not a Node error",{cause:e})}var T=(s=>(s.BlockDevice="BlockDevice",s.CharacterDevice="CharacterDevice",s.Directory="Directory",s.FIFO="FIFO",s.File="File",s.Socket="Socket",s.SymbolicLink="SymbolicLink",s))(T||{});function U(e){for(let t of J(T))if(e[`is${t}`]())return T[t];return"File"}var A=class extends Error{constructor(t,r){super(t,{cause:r}),this.name=this.constructor.name}},O=class extends Error{constructor(t,r){super(t,{cause:r}),this.name=this.constructor.name}},P=class extends Error{constructor(t,r){super(t,{cause:r}),this.name=this.constructor.name}},u=class extends Error{actualFileEntryType;constructor(t,r,o){let i=`${t}
|
|
2
|
-
was actually a ${r} instead of a Folder`;o?super(i,{cause:o}):super(i),this.name=this.constructor.name,this.actualFileEntryType=r}};var E=class extends Error{actualFileEntryType;constructor(t,r,o){let i=`${t}
|
|
3
|
-
was actually a ${r} instead of a Folder`;o?super(i,{cause:o}):super(i),this.name=this.constructor.name,this.actualFileEntryType=r}};import{ResultAsync as W}from"neverthrow";function w(e){return W.fromThrowable(async()=>{let t=await V(e.path);if(t.isDirectory())return t;throw new E(e.path,U(t))},t=>(t instanceof E||F(t),t))()}function _(e){return W.fromThrowable(async()=>{let t=await V(e.path);if(t.isFile())return t;throw new u(e.path,U(t))},t=>(t instanceof u||F(t),t))()}import{homedir as wt}from"node:os";import{inspect as bt}from"node:util";import{emptyDir as Tt,ensureDir as At,remove as Ot}from"fs-extra/esm";import{basename as X,dirname as Pt,resolve as K}from"pathe";var h=class e{#t;#e;#r;#o;constructor(t,...r){let o=t instanceof e?t.path:t;this.#t=K(o,...r),this.#e=X(this.#t),this.#e||(this.#e=this.#t),this.#o=Pt(this.path),this.#r=X(this.#o),this.#r||(this.#r=this.#e)}get path(){return this.#t}get name(){return this.#e}get parentName(){return this.#r}get parentPath(){return this.#o}get info(){return{path:this.#t,name:this.#e,parentName:this.#r,parentPath:this.#o}}relativePath=(t=N())=>this.#t.startsWith(t)?this.#t.slice(t.length+1):this.#t;toString=()=>this.#t;toJSON=()=>({Folder:this.info});[bt.custom]=()=>this.toJSON();getStats=()=>w(this);exists=async()=>(await this.getStats()).isOk();ensureExists=()=>At(this.#t);ensureEmpty=()=>Tt(this.#t);delete=()=>Ot(this.#t);getParentFolder=()=>new e(this.#o);subFolder=(t,...r)=>{let o=t instanceof e?t.path:t;return new e(this.#t,o,...r)};childFolders=t=>M(this,t,1);findFolders=t=>M(this,t)};function l(e,...t){return e===void 0?new h(""):new h(e,...t)}function Nt(...e){return new h(wt(),...e)}function N(){return K(process.cwd())}import{globby as It}from"globby";var j="**",vt={absolute:!0,gitignore:!0};async function q(e,t,r,o){let i={cwd:t.path,...vt,...r},a=j;if(typeof o=="string"||Array.isArray(o))a=o;else{let{patterns:n,extensions:s,expandDirectories:d,...m}=o;n&&(a=n),i={expandDirectories:s?{extensions:s}:!0,...m,...i}}return(await It(a,i)).sort().map(n=>e(n))}function f(e,t,r=j,o=Number.POSITIVE_INFINITY){return w(t).map(()=>q(e,t,{deep:o},r))}function M(e,t=j,r=Number.POSITIVE_INFINITY){return w(e).map(()=>q(l,e,{onlyDirectories:!0,deep:r},t))}import{readFile as Rt}from"node:fs/promises";import{outputFile as Dt}from"fs-extra/esm";import{ResultAsync as H}from"neverthrow";var B=/\n|\r|\r\n/;function Q(e){return H.fromThrowable(()=>Rt(e,"utf8"),t=>(F(t),t.code==="ENOENT"?new A(e,t):t.code==="EISDIR"?new u(e,"Directory",t):new O(e,t)))()}function tt(e,t){return H.fromThrowable(()=>Dt(e,t),r=>(F(r),r.code==="EISDIR"?new u(e,"Directory",r):new P(e,r)))()}import{ensureFile as Mt,ensureLink as jt,ensureSymlink as Ct,move as Yt,remove as zt}from"fs-extra/esm";import{basename as et,dirname as Lt,resolve as Zt}from"pathe";var c=class e{#t;#e;#r;#o;#i;#s;constructor(t,...r){let o=rt(t);this.#t=Zt(o,...r),this.#e=et(this.#t);let i=this.#e.lastIndexOf(".");i<=0?(this.#r=this.#e,this.#o=""):(this.#r=this.#e.slice(0,i),this.#o=this.#e.slice(i+1)),this.#s=Lt(this.path),this.#i=et(this.#s),this.#i||(this.#i=this.#e)}get path(){return this.#t}get fullName(){return this.#e}get name(){return this.#r}get ext(){return this.#o}get parentName(){return this.#i}get parentPath(){return this.#s}get info(){return{path:this.#t,fullName:this.#e,name:this.#r,ext:this.#o,parentName:this.#i,parentPath:this.#s}}relativePath=(t=N())=>this.#t.startsWith(t)?this.#t.slice(t.length+1):this.#t;toString=()=>this.#t;toJSON=()=>({File:this.info});[Ut.custom]=()=>this.toJSON();getParentFolder=()=>new h(this.#s);getStats=()=>_(this);exists=async()=>(await this.getStats()).isOk();ensureExists=()=>Mt(this.#t);copyTo=async t=>{let r=t instanceof e?t:g(t,this.#e);await r.getParentFolder().ensureExists(),await r.delete(),await Gt(this.#t,r.path,kt.COPYFILE_FICLONE)};moveTo=async t=>{let r=t instanceof e?t:g(t,this.#e);await r.getParentFolder().ensureExists(),await r.delete(),await Yt(this.#t,r.path)};linkTo=async t=>{let r=t instanceof e?t:g(t,this.#e);await r.getParentFolder().ensureExists(),await r.delete(),await jt(this.#t,r.path)};symlinkTo=async t=>{let r=t instanceof e?t:g(t,this.#e);await r.getParentFolder().ensureExists(),await r.delete(),await Ct(this.#t,r.path)};delete=()=>zt(this.#t);readText=()=>Q(this.path);readTextLines=()=>this.readText().map(t=>t.split(B));writeText=t=>tt(this.path,t)};function g(e,...t){return new c(e,...t)}function Jt(e,...t){return new c($t(),rt(e),...t)}function rt(e){return e instanceof c||e instanceof h?e.path:e}function Vt(e=l(),t){return f(g,e,t)}import{inspect as Wt}from"node:util";import{fromThrowable as _t}from"neverthrow";var C=class extends Error{nestedErrors;constructor({cause:t,nestedErrors:r}){super(`ZodParseError: ${Wt(r)}`,{cause:t}),this.nestedErrors=r}};function I(e,t){return _t(()=>e.parse(t),r=>{let o=r;return new C({cause:o,nestedErrors:o.format(i=>({...i,actual:Xt(t,i.path)}))})})()}function Xt(e,t){let r=e[t[0]];for(let o of t)r=r[o];return r}import{fromThrowable as ot}from"neverthrow";import Kt from"parse-json";import{configure as qt}from"safe-stable-stringify";var Ht=ot(e=>Kt(e),e=>e),Bt=qt({circularValue:Error,strict:!0}),Qt=ot((e,t)=>{let r=Bt(e,null,t);if(r===void 0)throw new Error("Undefined returned from stringify!!!");if(r.trim().length===0)throw new Error("Empty string returned from stringify!!!");return r},e=>e),x=class extends c{fileSchema;constructor(t,r,...o){super(r,...o),this.fileSchema=t}validateUnknown=t=>I(this.fileSchema,t);read=()=>this.readText().andThen(Ht).andThen(this.validateUnknown);write=(t,r=2)=>this.validateUnknown(t).andThen(o=>Qt(o,r)).asyncAndThen(this.writeText)};function it(e,t,...r){return new x(e,t,...r)}function te(e,t=l(),r="*.json"){return f(o=>it(e,o),t,r)}import{fromThrowable as st}from"neverthrow";import{parse as ee,stringify as Y}from"yaml";var nt=["yaml","yml"],re=st((e,t)=>ee(e,t),e=>e),oe=st((e,t=2)=>typeof t=="string"?Y(e,{indent:Number.parseInt(t),sortMapEntries:!0}):typeof t=="number"?Y(e,{indent:t,sortMapEntries:!0}):Y(e,{sortMapEntries:!0,...t}),e=>e),v=class extends x{read=t=>this.readText().andThen(r=>re(r,t)).andThen(r=>this.validateUnknown(r));write=(t,r)=>this.validateUnknown(t).andThen(o=>oe(o,r)).asyncAndThen(this.writeText)};function at(e,t,...r){return new v(e,t,...r)}function ie(e,t=l(),r={extensions:nt}){return f(o=>at(e,o),t,r)}import se from"sharp";import ne from"sharp-phash";var pt=6,lt=".avif",mt=["avif","gif","jpeg","jpg","png","svg","tiff","webp"],R=class extends c{sharp;constructor(t,...r){super(t,...r),this.sharp=se(this.path)}convertToAvif=async({newFolder:t,newName:r,height:o,width:i,effort:a=9,quality:n}={})=>{let s=t||this.getParentFolder(),d=r??this.name;d.endsWith(lt)||(d+=lt);let m=L(s,d);return await m.exists()||(await m.getParentFolder().ensureExists(),await this.sharp.resize({fit:"contain",height:o,width:i,withoutEnlargement:!0}).avif({effort:a,quality:n}).toFile(m.path)),m};getPhash=async()=>{let t=await ne(this.path);return ct(t)}};function L(e,...t){return new R(e,...t)}function ae(e=l(),t={extensions:mt}){return f(L,e,t)}function ct(e){return Number.parseInt(e,2).toString(36)}function z(e){return Number.parseInt(e,36).toString(2)}function pe(e,t){if(t.includes(e))return{case:"Exact",phash:e};let o=z(e);for(let i of t){let a=z(i),n=0,s=o.length;for(;s--&&n<=pt;)n+=Math.abs(o.charCodeAt(s)-a.charCodeAt(s));if(n<pt)return{case:"Similar",phash:i}}return{case:"Unique",phash:e}}var ht=(o=>(o.Exact="Exact",o.Similar="Similar",o.Unique="Unique",o))(ht||{});import p from"zod";var Pr=p.object({codec_type:p.literal("data")}),le=p.object({codec_type:p.literal("audio")}),me=p.object({codec_type:p.literal("video"),height:p.number().int().positive(),width:p.number().int().positive()}),ft=p.object({format:p.object({bit_rate:p.coerce.number(),duration:p.coerce.number(),size:p.coerce.number()}),streams:p.array(p.union([le,me]))}).transform(({format:{bit_rate:e,duration:t,size:r},streams:o},{addIssue:i})=>{for(let a of o)if(a.codec_type==="video"){let{height:n,width:s}=a,d=Math.round(t*1e3),m=Math.trunc(t%60),b=Math.trunc(t/60%60),$=Math.trunc(t/60/60%60);return{bitRate:e,bytes:r,height:n,hours:$,millis:d,minutes:b,seconds:m,width:s}}return i({code:p.ZodIssueCode.custom,message:"Video was missing video stream"}),p.NEVER});import ut from"fluent-ffmpeg";import{ResultAsync as dt}from"neverthrow";var D=class extends Error{constructor(t,r){super(t,{cause:r}),this.name=this.constructor.name}},k=class extends Error{constructor(t,r){super(t,{cause:r}),this.name=this.constructor.name}};var gt=["avi","flv","mkv","mov","mp4","mpeg","mpg","rm","rmvb","webm","wmv"],ce=e=>new Promise((t,r)=>ut.ffprobe(e,(o,i)=>{o&&r(o),t(i)})),he=(e,t,r,o)=>new Promise((i,a)=>ut(e).on("error",n=>a(n)).on("end",()=>i()).screenshot({filename:r,folder:t,timestamps:typeof o=="string"?[o]:[o]})),G=class extends c{getMetaData=()=>this.getStats().andThen(()=>dt.fromThrowable(()=>ce(this.path),t=>new D(this.path,t))().andThen(t=>I(ft,t)));getThumbnail=(t,r)=>dt.fromThrowable(async()=>{if(await r.exists())return r;let i=r.getParentFolder();return await i.ensureExists(),await he(this.path,i.path,`${r.name}.png`,t),r},o=>new k(this.path,o))()};function yt(e,...t){return new G(e,...t)}function fe(e=l(),t={extensions:gt}){return f(yt,e,t)}import y from"log-update";import{CheckRepoActions as de,simpleGit as Ft}from"simple-git";var ue=e=>e.padEnd(11),S="";function ge({method:e,stage:t,progress:r,processed:o,total:i}){let a=e.toUpperCase(),n=ue(t);if(n!==S){S&&(y(a,S,"completed"),y.done()),S=n;return}let s=i.toString().length,d=process.stdout.columns,m=Math.max(1,Math.ceil(d*r/100)),b=d-m,$="=".repeat(m),xt="_".repeat(b),St=[a,n,o.toString().padStart(s),"of",i.toString(),`(${r.toString().padStart(2)}%)`].join(" "),Et=`${$}${xt}`;y(`${St}
|
|
4
|
-
${Et}`)}var Z=class extends h{owner;repo;baseUrl;shortUrl;fullUrl;progress;constructor({owner:t,repo:r,onProgress:o,baseUrl:i},a,...n){let s=[...n,t,r];super(a,...s),this.owner=t,this.repo=r,this.baseUrl=i??"https://github.com",this.shortUrl=`${t}/${r}`,this.fullUrl=`${this.baseUrl}/${this.shortUrl}`,this.progress=o??ge}get git(){return Ft({baseDir:this.path,progress:this.progress})}isaRepo=async()=>await Ft(this.path).checkIsRepo(de.IS_REPO_ROOT);clone=async()=>{if(console.log(`Cloning
|
|
5
|
-
`,this.fullUrl,`
|
|
6
|
-
into:
|
|
7
|
-
`,this.relativePath()),await this.ensureExists(),await this.isaRepo()){console.log(this.fullUrl,"is already cloned");return}await this.git.clone(this.fullUrl,this.path),y("CLONE",S,"completed"),y.done(),console.log(`Cloned
|
|
8
|
-
`,this.fullUrl,`
|
|
9
|
-
into:
|
|
10
|
-
`,this.relativePath())};pull=async()=>{console.log("Pulling",this.fullUrl),await this.ensureExists(),await this.isaRepo()||(console.log(this.fullUrl,"does not exist yet, cloning..."),await this.clone());let r=await this.git.pull();return y("PULL",S,"completed"),y.done(),console.log("Pulled",this.fullUrl),r};getReadMe=()=>g(this.path,"README.md")};export{c as AFile,h as Folder,Z as GitFolder,mt as IMAGE_EXTENSIONS,R as ImageFile,x as JsonFile,ht as PhashSimilarity,gt as VIDEO_EXTENSIONS,G as VideoFile,nt as YAML_EXTENSIONS,v as YamlFile,g as afile,ct as base2to36,z as base36to2,N as cwd,Vt as findFiles,ae as findImageFiles,te as findJsonFiles,fe as findVideoFiles,ie as findYamlFiles,l as folder,Jt as homeFile,Nt as homeFolder,L as imageFile,it as jsonFile,pe as phashCheck,yt as videoFile,at as yamlFile};
|
|
1
|
+
import{constants as e,createReadStream as t,createWriteStream as n}from"node:fs";import{chmod as r,copyFile as i,opendir as a,readFile as o,stat as s}from"node:fs/promises";import{homedir as c,platform as l,tmpdir as u}from"node:os";import{inspect as d}from"node:util";import{fcmd as f}from"fluent-command";import{appendFile as ee}from"fs-extra";import{emptyDir as te,ensureDir as ne,ensureFile as re,ensureLink as ie,ensureSymlink as ae,move as oe,outputFile as p,remove as m}from"fs-extra/esm";import{ResultAsync as h}from"neverthrow";import g,{basename as se}from"pathe";import ce from"sharp";import le from"sharp-phash";import{parseString as ue,zparse as de,zstringify as fe}from"zerde";import _ from"zod";import pe from"picomatch";import{pipeline as me}from"node:stream/promises";const he={Append:`a`,AppendOrFail:`ax`,AppendAndRead:`a+`,AppendAndReadOrFail:`ax+`,Read:`r`,ReadAndWrite:`r+`,Write:`w`,WriteOrFailIfExists:`wx`};function v(...e){let t=e.map((e,t)=>{if(typeof e==`number`)return e.toString();if(t===0&&e===`~`)return c();if(e.toUpperCase()!==e)return e;let n=``;if(e.startsWith(`$`)&&(n=e.slice(1)),l()===`win32`&&e.startsWith(`%`)&&e.endsWith(`%`)&&(n=e.slice(1,-1)),n!==``){let t=process.env[n];if(!t)throw Error(`Missing Environment variable ${e}`);return t}return e});return g.resolve(...t)}function y(e){let t=e.lastIndexOf(`.`);return t<0?e:e.slice(t+1)}function ge(e){return y(g.extname(e))}const _e=4,ve=-5;var b=class extends Error{operation;path;constructor(e,t){super(),this.path=e,this.operation=this.constructor.name.slice(_e,ve),this.message=`Error trying to ${this.operation} file: ${e}`,this.cause=t,this.name=this.constructor.name}},x=class extends b{url;constructor(e,t,n){super(e,n),this.url=t,this.message=`Error trying to download url: ${t} to file: ${e}`}},S=class extends b{},C=class extends b{},w=class extends b{},T=class extends b{},E=class extends b{},D=class extends b{},O=class extends b{},k=class extends b{},A=class extends b{},j=class extends b{};const M=`DESTINATION_FALLBACK`;var N=class extends b{destinationPath;constructor(e,t,n){super(e,n),this.destinationPath=t??M}},P=class extends N{},F=class extends N{},I=class extends N{},L=class extends N{};const ye=6,be=[`avif`,`gif`,`jpeg`,`jpg`,`png`,`svg`,`tiff`,`webp`],R=64,xe=16,z=/^[01]+$/,B=_.codec(_.string().regex(z).length(R),_.hex().brand(`PhashString`),{decode:e=>BigInt(`0b${e}`).toString(xe),encode:e=>BigInt(`0x${e}`).toString(2).padStart(R,`0`)}),V=_.int().nonnegative().max(R).brand(`PhashThreshold`),Se=6,H=V.parse(Se);function Ce(e,t,n=H){if(e===t)return Object.freeze({level:`SAME`,diff:0});let r=B.encode(e),i=B.encode(t),a=0,o=R;for(;o--;)r[o]!==i[o]&&a++;let s=a<n?`SIMILAR`:`DIFFERENT`;return Object.freeze({level:s,diff:a})}function we(e,t,n=H){if(e===t)return!0;let r=B.encode(e),i=B.encode(t),a=0,o=R;for(;o--;)if(r[o]!==i[o]&&a++,a>=n)return!1;return!0}const Te=_.coerce.number().positive(),U=_.coerce.number().int(),Ee=U.nonnegative(),W=U.positive(),G=_.object({duration:Te,bit_rate:W}),De=1e3;function K(e){let t=Math.round(e*De),n=Math.trunc(e%60),r=Math.trunc(e/60%60),i=Math.trunc(e/60/60%60),a=[i,r,n].filter((e,t)=>t>0||e>0).map((e,t)=>{let n=e.toString();return t>0&&(n=n.padStart(2,`0`)),n}).join(`:`);return{millis:t,seconds:n,minutes:r,hours:i,hms:a}}const q=_.object({...G.shape,codec_name:_.string(),codec_long_name:_.string(),codec_tag_string:_.string(),nb_frames:W}),Oe=_.object({...q.shape,codec_type:_.literal(`audio`),channels:W,channel_layout:_.string()}),ke=_.object({...q.shape,codec_type:_.literal(`video`),height:W,width:W}),Ae=_.object({...G.shape,nb_streams:W,format_name:_.string(),format_long_name:_.string(),size:W}),je=_.object({format:Ae,streams:_.array(_.union([Oe,ke])).min(2)}).transform(({format:{duration:e,...t},streams:n},{addIssue:r})=>{let i=n.filter(e=>e.codec_type===`video`).map(({duration:e,...t})=>({...t,...K(e)})).at(0);if(!i)return r({code:`custom`,message:`Video was missing video stream`}),_.NEVER;let a=n.filter(e=>e.codec_type===`audio`).map(({duration:e,...t})=>({...t,...K(e)}));return a.length===0?(r({code:`custom`,message:`Video was missing audio stream`}),_.NEVER):{videoStream:i,audioStreams:a,...t,...K(e)}}),Me=[`avi`,`flv`,`mkv`,`mov`,`mp4`,`mpeg`,`mpg`,`rm`,`rmvb`,`webm`,`wmv`];function Ne(e){return f(`ffprobe`).opt(`hide_banner`).opt(`show_format`).opt(`show_streams`).opt({v:`error`,print_format:`json`}).args(e).read().andThen(e=>de(e.stdout,je,`json`))}var J=class extends Error{operation;path;constructor(e,t,n){let r=`Error trying to ${e} folder: ${t}`;super(r,{cause:n}),this.path=t,this.operation=e,this.name=this.constructor.name}},Pe=class extends J{constructor(e,t){super(`glob`,e,t)}};const Fe={CACHE:{darwin:[`~`,`Library`,`Caches`],linux:[`~`,`.cache`],win32:[`$LOCALAPPDATA`]},CONFIG:{darwin:[`~`,`Library`,`Preferences`],linux:[`~`,`.config`],win32:[`$APPDATA`]},DATA:{darwin:[`~`,`Library`,`Application Support`],linux:[`~`,`.local`,`share`],win32:[`$LOCALAPPDATA`]},STATE:{darwin:[`~`,`Library`,`Logs`],linux:[`~`,`.local`,`state`],win32:[`$LOCALAPPDATA`]}};function Y(e){let t=l();if(t===`win32`||t===`darwin`)return Fe[e][t];let n=process.env[`XDG_${e}_HOME`];return n?[n]:Fe[e].linux}function Ie(...e){return Q(...Y(`CACHE`),...e)}function Le(...e){return Q(...Y(`CONFIG`),...e)}function Re(...e){return Q(...Y(`DATA`),...e)}function ze(...e){return Q(...Y(`STATE`),...e)}function Be(...e){return Q(c(),...e)}function Ve(...e){return Q(u(),...e)}function X(){return Q(process.cwd())}var Z=class{path;name;constructor(...e){this.path=v(...e),this.name=se(this.path),this.name||=this.path}get info(){return{path:this.path,name:this.name}}folder=(...e)=>Q(this.path,...e);file=(e,...t)=>$(this.path,e,...t);relativePath=(e=X())=>{let t=this.path;return t.startsWith(e.path)&&(t=t.slice(e.path.length),t.startsWith(`/`))?t.slice(1):t};toString=()=>this.path;toJSON=()=>({FluentFolder:this.info});[d.custom]=()=>this.toJSON();stats=h.fromThrowable(async()=>{let e=await s(this.path);if(!e.isDirectory())throw new J(`stat`,this.path,`FolderWasNotFolder`);return e},e=>e instanceof J?e:new J(`stat`,this.path,e));exists=async()=>(await this.stats()).isOk();ensureExists=()=>ne(this.path);ensureEmpty=()=>te(this.path);remove=()=>m(this.path);findFolders=h.fromThrowable(async(e={})=>{let t=e.glob?pe(e.glob,{cwd:this.path}):null,n=await a(this.path,{recursive:e.recursive??!0}),r=[];for await(let e of n){if(!e.isDirectory())continue;let n=g.resolve(e.parentPath,e.name);if(t&&!t(n))continue;r.push(n)}return r.sort().map(e=>Q(e))},e=>new Pe(this.path,e));findFiles=h.fromThrowable(async(e={})=>{let t=e.glob?pe(e.glob,{cwd:this.path}):null,n=await a(this.path,{recursive:e.recursive??!0}),r=[],i=(e.exts??[]).map(y);for await(let e of n){if(!e.isFile())continue;let n=g.resolve(e.parentPath,e.name);if(i.length>0){let t=ge(e.name);if(!i.includes(t))continue}if(t&&!t(n))continue;r.push(n)}return r.sort().map(e=>$(e))},e=>new Pe(this.path,e))};function Q(...e){return new Z(...e)}const He=h.fromThrowable(async(e,t,n={})=>{try{let{overwrite:r=!1,progressThrottle:i=100,onProgress:a}=n;if(!r&&await t.exists())return`ALREADY_EXISTS`;await t.folder().ensureExists();let o=await fetch(e);if(!o.ok){let{status:t,statusText:n,redirected:r,type:i}=o,a={};throw o.headers.forEach((e,t)=>{a[t]=e}),Error(`Got ${o.status} ${o.statusText} for url: ${e}`,{cause:{status:t,statusText:n,headers:a,redirected:r,type:i}})}let s=W.parse(o.headers.get(`content-length`));if(!o.body)throw Error(`Request body was null for url: ${e}`,{cause:o});let c=o.body,l=t.createWriteStream();if(!a)return await me(c,l),`SUCCESS`;let u=0,d=null,f=e=>{u+=e,!d&&(a({downloadedBytes:u,totalBytes:s}),d=setTimeout(()=>{d=null},i))};return await me(c,async function*(e){for await(let t of e)f(t.length)},l),`SUCCESS`}catch(n){throw new x(t.path(),e,n)}},e=>e);var Ue=class a{#path;#folderPath;#name;#basename;#ext;#schema;#info;#ensureDestination=async e=>{let t=e instanceof a?e:e.file(this.#name);return await t.folder().ensureExists(),await t.remove(),t.path()};#prepareTarget=async e=>{let{newFolder:t=this.folder(),newBaseName:n=this.#basename,newExt:r=this.#ext}=e,i=t.file(`${n}.${r}`);return await i.remove(),await t.ensureExists(),i};constructor(e,t,...n){this.#path=v(t,...n),this.#schema=e;let r=We(this.#path);this.#folderPath=r.folderPath,this.#name=r.name,this.#basename=r.basename,this.#ext=r.ext,this.#info=r}toString=()=>this.#path;toJSON=()=>({FluentFile:this.info});[d.custom]=()=>this.toJSON();path=()=>this.#path;folderPath=()=>this.#folderPath;name(e){return e===void 0?this.#name:(this.#name=e,this.#path=`${this.#folderPath}/${e}`,this)}basename(e){return e===void 0?this.#basename:(this.#basename=e,this.#name=e+this.#ext,this.#path=`${this.#folderPath}/${this.#name}`,this)}ext(e){return e===void 0?this.#ext:(this.#ext=e,this.#name=this.#basename+e,this.#path=`${this.#folderPath}/${this.#name}`,this)}schema=e=>new a(e,this.#path);get info(){return this.#info}relativePath=(e=X())=>{let t=this.#path;return t.startsWith(e.path)&&(t=t.slice(e.path.length),t.startsWith(`/`))?t.slice(1):t};file=(e,...t)=>new a(this.#schema,this.#folderPath,e,...t);folder=(...e)=>new Z(this.#folderPath,...e);stats=h.fromThrowable(async()=>{let e=await s(this.#path);if(!e.isFile())throw new A(this.#path,`FileWasNotFile`);return e},e=>e instanceof A?e:new A(this.#path,e));exists=async()=>(await this.stats()).isOk();ensureExists=()=>re(this.#path);chmod=h.fromThrowable(async e=>{await r(this.#path,e)},e=>new C(this.#path,e));copyTo=h.fromThrowable(async t=>{let n=null;try{n=await this.#ensureDestination(t),await i(this.#path,n,e.COPYFILE_FICLONE)}catch(e){throw new P(this.#path,n,e)}},e=>e);moveTo=h.fromThrowable(async(e,t)=>{let n=null;try{n=await this.#ensureDestination(e),await oe(this.#path,n,{overwrite:!0,dereference:t})}catch(e){throw new F(this.#path,n,e)}},e=>e);linkTo=h.fromThrowable(async e=>{let t=null;try{t=await this.#ensureDestination(e),await ie(this.#path,t)}catch(e){throw new I(this.#path,t,e)}},e=>e);symlinkTo=h.fromThrowable(async e=>{let t=null;try{t=await this.#ensureDestination(e),await ae(this.#path,t)}catch(e){throw new L(this.#path,t,e)}},e=>e);remove=h.fromThrowable(()=>m(this.#path),e=>new k(this.#path,e));readText=h.fromThrowable((e={})=>o(this.#path,{encoding:`utf8`,...e}),e=>new O(this.#path,e));readBuffer=h.fromThrowable((e={})=>o(this.#path,e),e=>new O(this.#path,e));read=(e={})=>this.readText(e).andThen(t=>de(t,this.#schema,{format:this.#path,...e}));createReadStream=e=>t(this.#path,e);writeText=h.fromThrowable((e,t={})=>p(this.#path,e,t),e=>new j(this.#path,e));writeBuffer=h.fromThrowable((e,t={})=>p(this.#path,e,t),e=>new j(this.#path,e));write=(e,t={})=>fe(e,this.#schema,t).andThen(e=>this.writeText(e,t));createWriteStream=e=>n(this.#path,e);append=h.fromThrowable((e,t={})=>ee(this.#path,e,t),e=>new S(this.#path,e));image=(e={})=>{let t=ce(this.#path,e),n=h.fromThrowable(()=>t.metadata(),e=>new T(this.#path,e)),r=h.fromThrowable(async e=>{let n=await this.#prepareTarget(e);return await t.resize(e).toFile(n.path()),n},e=>new D(this.#path,e)),i=h.fromThrowable(async(e={})=>{let n=await this.#prepareTarget({...e,newExt:`avif`});return await t.resize(e).avif(e).toFile(n.path()),n},e=>new w(this.#path,e)),a=h.fromThrowable(async()=>{let t=await le(this.#path,e);return B.decode(t)},e=>new E(this.#path,e));return{...t,metadata:n,resize:r,toAVIF:i,phash:a}};video=()=>({metadata:()=>Ne(this.#path),extractFrame:async(e={})=>{let{time:t=1,unit:n=`s`,ext:r=`png`,...i}=e,a=n===`s`?t:`${t}${n}`,o=await this.#prepareTarget({newExt:r,...i});return f(`ffmpeg`).opt(`hide_banner`).opt({ss:a,i:this.#path,"frames:v":1,y:``}).args(o.path()).read().map(()=>o)}});download=(e,t={})=>He(e,this,t)};function We(e){let t=g.basename(e),n=g.dirname(e),r=``,i=``,a=t.lastIndexOf(`.`);return a<=0?(r=t,i=``):(r=t.slice(0,a),i=t.slice(a+1)),{absolutePath:e,folderPath:n,name:t,basename:r,ext:i}}function $(e,...t){return new Ue(ue(),e,...t)}function Ge(e,...t){return $(c(),e,...t)}export{H as DEFAULT_PHASH_THRESHOLD,M as DESTINATION_FALLBACK,S as FileAppendError,C as FileChmodError,P as FileCopyError,x as FileDownloadError,w as FileImageConvertError,T as FileImageMetadataError,E as FileImagePhashError,D as FileImageResizeError,I as FileLinkError,F as FileMoveError,O as FileReadError,k as FileRemoveError,A as FileStatError,L as FileSymLinkError,j as FileWriteError,Ue as FluentFile,Z as FluentFolder,he as FsFlags,be as IMAGE_EXTENSIONS,ye as MAX_DIFFERENCES,Me as VIDEO_EXTENSIONS,z as binaryStringRegex,Ie as cacheFolder,Ce as comparePhashes,Le as configFolder,X as currentFolder,Re as dataFolder,$ as ffile,Q as folder,Ne as getVideoMetaData,Ge as homeFile,Be as homeFolder,ze as logsFolder,B as phashStringSchema,V as phashThresholdSchema,we as phashesMatch,Ve as tempFolder};
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluent-file",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A fluent TypeScript library for working with files and folders",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"files",
|
|
7
7
|
"folders",
|
|
8
8
|
"fluent"
|
|
9
9
|
],
|
|
10
|
-
"repository":
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/dannywexler/fluent-file.git"
|
|
13
|
+
},
|
|
11
14
|
"author": "Danny Wexler",
|
|
12
15
|
"license": "MIT",
|
|
13
16
|
"type": "module",
|
|
@@ -23,93 +26,37 @@
|
|
|
23
26
|
"files": [
|
|
24
27
|
"dist"
|
|
25
28
|
],
|
|
26
|
-
"overrides": {
|
|
27
|
-
"esbuild": "0.24.0"
|
|
28
|
-
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"
|
|
30
|
+
"@standard-schema/spec": "^1.0.0",
|
|
31
|
+
"fluent-command": "^0.1.4",
|
|
31
32
|
"fs-extra": "^11.2.0",
|
|
32
|
-
"globby": "^14.0.2",
|
|
33
|
-
"log-update": "^6.1.0",
|
|
34
33
|
"neverthrow": "^8.1.1",
|
|
35
|
-
"parse-json": "^8.1.0",
|
|
36
34
|
"pathe": "^1.1.2",
|
|
37
|
-
"
|
|
38
|
-
"sharp": "^0.
|
|
35
|
+
"picomatch": "^4.0.3",
|
|
36
|
+
"sharp": "^0.34.5",
|
|
39
37
|
"sharp-phash": "^2.2.0",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"zod": "^3.24.1"
|
|
38
|
+
"zerde": "^0.1.4",
|
|
39
|
+
"zod": "^4.1.13"
|
|
43
40
|
},
|
|
44
41
|
"devDependencies": {
|
|
45
|
-
"@biomejs/biome": "
|
|
42
|
+
"@biomejs/biome": "2.2.6",
|
|
46
43
|
"@types/download": "^8.0.5",
|
|
47
44
|
"@types/fluent-ffmpeg": "^2.1.27",
|
|
48
45
|
"@types/fs-extra": "^11.0.4",
|
|
49
46
|
"@types/node": "^22.10.2",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
47
|
+
"@types/picomatch": "^4.0.2",
|
|
48
|
+
"lefthook": "^1.13.6",
|
|
49
|
+
"tsdown": "^0.12.9",
|
|
50
|
+
"typescript": "^5.9.3",
|
|
53
51
|
"vite-tsconfig-paths": "^5.1.4",
|
|
54
|
-
"vitest": "^2.
|
|
55
|
-
"wireit": "^0.14.9"
|
|
52
|
+
"vitest": "^3.2.4"
|
|
56
53
|
},
|
|
57
54
|
"scripts": {
|
|
58
|
-
"build": "
|
|
59
|
-
"test": "wireit",
|
|
55
|
+
"build": "lefthook run build",
|
|
60
56
|
"test:watch": "vitest"
|
|
61
57
|
},
|
|
62
|
-
"wireit": {
|
|
63
|
-
"lint": {
|
|
64
|
-
"command": "biome check --error-on-warnings --colors=force --write src",
|
|
65
|
-
"files": [
|
|
66
|
-
"src"
|
|
67
|
-
],
|
|
68
|
-
"output": []
|
|
69
|
-
},
|
|
70
|
-
"check": {
|
|
71
|
-
"command": "tsc --build --pretty",
|
|
72
|
-
"dependencies": [
|
|
73
|
-
"lint"
|
|
74
|
-
],
|
|
75
|
-
"files": [
|
|
76
|
-
"src/**/*.ts",
|
|
77
|
-
"tsconfig.json"
|
|
78
|
-
],
|
|
79
|
-
"output": [
|
|
80
|
-
"tsconfig.tsbuildinfo"
|
|
81
|
-
]
|
|
82
|
-
},
|
|
83
|
-
"test": {
|
|
84
|
-
"command": "vitest run",
|
|
85
|
-
"dependencies": [
|
|
86
|
-
"check"
|
|
87
|
-
],
|
|
88
|
-
"files": [
|
|
89
|
-
"src",
|
|
90
|
-
"test"
|
|
91
|
-
],
|
|
92
|
-
"output": []
|
|
93
|
-
},
|
|
94
|
-
"build": {
|
|
95
|
-
"command": "tsup",
|
|
96
|
-
"dependencies": [
|
|
97
|
-
"test"
|
|
98
|
-
],
|
|
99
|
-
"files": [
|
|
100
|
-
"src",
|
|
101
|
-
"tsconfig.json",
|
|
102
|
-
"tsconfig.tsbuildinfo",
|
|
103
|
-
"tsup.config.ts"
|
|
104
|
-
],
|
|
105
|
-
"output": [
|
|
106
|
-
"dist"
|
|
107
|
-
]
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
58
|
"trustedDependencies": [
|
|
111
59
|
"@biomejs/biome",
|
|
112
|
-
"esbuild",
|
|
113
60
|
"sharp"
|
|
114
61
|
]
|
|
115
62
|
}
|