@ryuu-reinzz/haruka-lib 1.2.21 → 2.0.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/main/index.js +4 -1
- package/main/socket.js +1 -2
- package/main/sticker-engine/java-script/Sticker.d.ts +130 -0
- package/main/sticker-engine/java-script/Sticker.js +187 -0
- package/main/sticker-engine/java-script/Types.d.ts +43 -0
- package/main/sticker-engine/java-script/Types.js +1 -0
- package/main/sticker-engine/java-script/Utils.d.ts +9 -0
- package/main/sticker-engine/java-script/Utils.js +11 -0
- package/main/sticker-engine/java-script/extractMetadata.d.ts +6 -0
- package/main/sticker-engine/java-script/extractMetadata.js +11 -0
- package/main/sticker-engine/java-script/index.d.ts +8 -0
- package/main/sticker-engine/java-script/index.js +8 -0
- package/main/sticker-engine/java-script/internal/Metadata/Exif.d.ts +9 -0
- package/main/sticker-engine/java-script/internal/Metadata/Exif.js +34 -0
- package/main/sticker-engine/java-script/internal/Metadata/RawMetadata.d.ts +8 -0
- package/main/sticker-engine/java-script/internal/Metadata/RawMetadata.js +9 -0
- package/main/sticker-engine/java-script/internal/Metadata/StickerMetadata.d.ts +18 -0
- package/main/sticker-engine/java-script/internal/Metadata/StickerMetadata.js +49 -0
- package/main/sticker-engine/java-script/internal/Metadata/StickerTypes.d.ts +7 -0
- package/main/sticker-engine/java-script/internal/Metadata/StickerTypes.js +8 -0
- package/main/sticker-engine/java-script/internal/convert.d.ts +3 -0
- package/main/sticker-engine/java-script/internal/convert.js +69 -0
- package/main/sticker-engine/java-script/internal/crop.d.ts +2 -0
- package/main/sticker-engine/java-script/internal/crop.js +30 -0
- package/main/sticker-engine/java-script/internal/imagesToWebp.d.ts +2 -0
- package/main/sticker-engine/java-script/internal/imagesToWebp.js +17 -0
- package/main/sticker-engine/java-script/internal/videoToGif.d.ts +3 -0
- package/main/sticker-engine/java-script/internal/videoToGif.js +16 -0
- package/main/sticker-engine/type-script/Sticker.ts +206 -0
- package/main/sticker-engine/type-script/Types.ts +189 -0
- package/main/sticker-engine/type-script/Utils.ts +12 -0
- package/main/sticker-engine/type-script/extractMetadata.ts +13 -0
- package/main/sticker-engine/type-script/index.ts +9 -0
- package/main/sticker-engine/type-script/internal/Metadata/Exif.ts +39 -0
- package/main/sticker-engine/type-script/internal/Metadata/RawMetadata.ts +15 -0
- package/main/sticker-engine/type-script/internal/Metadata/StickerMetadata.ts +60 -0
- package/main/sticker-engine/type-script/internal/Metadata/StickerTypes.ts +7 -0
- package/main/sticker-engine/type-script/internal/convert.ts +87 -0
- package/main/sticker-engine/type-script/internal/crop.ts +32 -0
- package/main/sticker-engine/type-script/internal/imagesToWebp.ts +19 -0
- package/main/sticker-engine/type-script/internal/node-webpmux.d.ts +8 -0
- package/main/sticker-engine/type-script/internal/videoToGif.ts +18 -0
- package/package.json +12 -3
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Categories, IStickerConfig, IStickerOptions } from '../../Types'
|
|
2
|
+
import Utils from '../../Utils'
|
|
3
|
+
|
|
4
|
+
export default class StickerMetadata implements IStickerOptions {
|
|
5
|
+
public crop = false
|
|
6
|
+
public full = false
|
|
7
|
+
constructor(
|
|
8
|
+
public pack = '',
|
|
9
|
+
public author = '',
|
|
10
|
+
public categories: Categories[] = [],
|
|
11
|
+
public id = Utils.generateStickerID()
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
static from = (object: Partial<StickerMetadata>): StickerMetadata => {
|
|
15
|
+
return new StickerMetadata(object.pack, object.author, object.categories, object.id)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public setPack = (title: string): this => {
|
|
19
|
+
this.pack = title
|
|
20
|
+
return this
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public setAuthor = (author: string): this => {
|
|
24
|
+
this.author = author
|
|
25
|
+
return this
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public setId = (id: string): this => {
|
|
29
|
+
this.id = id
|
|
30
|
+
return this
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public setCrop = (value: boolean): this => {
|
|
34
|
+
this.crop = value
|
|
35
|
+
this.full = !value
|
|
36
|
+
return this
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public setFull = (value: boolean): this => {
|
|
40
|
+
this.crop = !value
|
|
41
|
+
this.full = value
|
|
42
|
+
return this
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public setCategories = (categories: string | string[]): this => {
|
|
46
|
+
this.categories = (
|
|
47
|
+
typeof categories === 'string' ? categories.split(',').map((emoji) => emoji.trim()) : categories
|
|
48
|
+
) as Categories[]
|
|
49
|
+
return this
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public toJSON = (): IStickerConfig => {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
const obj: any = {}
|
|
55
|
+
Object.keys(this)
|
|
56
|
+
.filter((key) => typeof this[key as keyof this] !== 'function')
|
|
57
|
+
.forEach((key) => (obj[key] = this[key as keyof IStickerConfig] as IStickerConfig[keyof IStickerConfig]))
|
|
58
|
+
return obj as IStickerConfig
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import sharp, { fit } from 'sharp'
|
|
2
|
+
import videoToGif from './videoToGif'
|
|
3
|
+
import { writeFile } from 'fs-extra'
|
|
4
|
+
import { tmpdir } from 'os'
|
|
5
|
+
import crop from './crop'
|
|
6
|
+
import { StickerTypes } from './Metadata/StickerTypes'
|
|
7
|
+
import { defaultBg } from '../Utils'
|
|
8
|
+
import { IStickerOptions } from '..'
|
|
9
|
+
|
|
10
|
+
const convert = async (
|
|
11
|
+
data: Buffer,
|
|
12
|
+
mime: string,
|
|
13
|
+
{ quality = 100, background = defaultBg, type = StickerTypes.DEFAULT }: IStickerOptions
|
|
14
|
+
): Promise<Buffer> => {
|
|
15
|
+
const isVideo = mime.startsWith('video')
|
|
16
|
+
let image = isVideo ? await videoToGif(data) : data
|
|
17
|
+
const isAnimated = isVideo || mime.includes('gif') || mime.includes('webp')
|
|
18
|
+
|
|
19
|
+
if (isAnimated && ['crop', 'circle', 'rouded'].includes(type)) {
|
|
20
|
+
const filename = `${tmpdir()}/${Math.random().toString(36)}.webp`
|
|
21
|
+
await writeFile(filename, image)
|
|
22
|
+
;[image, type] = [
|
|
23
|
+
await crop(filename),
|
|
24
|
+
type === StickerTypes.CIRCLE
|
|
25
|
+
? StickerTypes.CIRCLE
|
|
26
|
+
: type === StickerTypes.ROUNDED
|
|
27
|
+
? StickerTypes.ROUNDED
|
|
28
|
+
: StickerTypes.DEFAULT
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const img = sharp(image, { animated: isAnimated }).toFormat('webp')
|
|
33
|
+
|
|
34
|
+
switch (type) {
|
|
35
|
+
case StickerTypes.CROPPED:
|
|
36
|
+
img.resize(512, 512, {
|
|
37
|
+
fit: fit.cover
|
|
38
|
+
})
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
case StickerTypes.FULL:
|
|
42
|
+
img.resize(512, 512, {
|
|
43
|
+
fit: fit.contain,
|
|
44
|
+
background
|
|
45
|
+
})
|
|
46
|
+
break
|
|
47
|
+
|
|
48
|
+
case StickerTypes.CIRCLE:
|
|
49
|
+
img.resize(512, 512, {
|
|
50
|
+
fit: fit.cover
|
|
51
|
+
}).composite([
|
|
52
|
+
{
|
|
53
|
+
input: Buffer.from(
|
|
54
|
+
`<svg width="512" height="512"><circle cx="256" cy="256" r="256" fill="${background}"/></svg>`
|
|
55
|
+
),
|
|
56
|
+
blend: 'dest-in',
|
|
57
|
+
gravity: 'northeast',
|
|
58
|
+
tile: true
|
|
59
|
+
}
|
|
60
|
+
])
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
case StickerTypes.ROUNDED:
|
|
64
|
+
img.resize(512, 512, {
|
|
65
|
+
fit: fit.cover
|
|
66
|
+
}).composite([
|
|
67
|
+
{
|
|
68
|
+
input: Buffer.from(
|
|
69
|
+
`<svg width="512" height="512"><rect rx="50" ry="50" width="512" height="512" fill="${background}"/></svg>`
|
|
70
|
+
),
|
|
71
|
+
blend: 'dest-in',
|
|
72
|
+
gravity: 'northeast',
|
|
73
|
+
tile: true
|
|
74
|
+
}
|
|
75
|
+
])
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return await img
|
|
80
|
+
.webp({
|
|
81
|
+
quality,
|
|
82
|
+
lossless: false
|
|
83
|
+
})
|
|
84
|
+
.toBuffer()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default convert
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Ffmpeg from 'fluent-ffmpeg'
|
|
2
|
+
import { readFile } from 'fs-extra'
|
|
3
|
+
import { tmpdir } from 'os'
|
|
4
|
+
|
|
5
|
+
const crop = async (filename: string): Promise<Buffer> => {
|
|
6
|
+
const file = await new Promise<string>((resolve) => {
|
|
7
|
+
const name = `${tmpdir()}/${Math.random().toString(36)}.webp`
|
|
8
|
+
Ffmpeg(filename)
|
|
9
|
+
// eslint-disable-next-line no-useless-escape
|
|
10
|
+
.outputOptions([
|
|
11
|
+
'-vcodec',
|
|
12
|
+
'libwebp',
|
|
13
|
+
'-vf',
|
|
14
|
+
// eslint-disable-next-line no-useless-escape
|
|
15
|
+
`crop=w='min(min(iw\,ih)\,500)':h='min(min(iw\,ih)\,500)',scale=500:500,setsar=1,fps=15`,
|
|
16
|
+
'-loop',
|
|
17
|
+
'0',
|
|
18
|
+
'-preset',
|
|
19
|
+
'default',
|
|
20
|
+
'-an',
|
|
21
|
+
'-vsync',
|
|
22
|
+
'0',
|
|
23
|
+
'-s',
|
|
24
|
+
'512:512'
|
|
25
|
+
])
|
|
26
|
+
.save(name)
|
|
27
|
+
.on('end', () => resolve(name))
|
|
28
|
+
})
|
|
29
|
+
return await readFile(file)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default crop
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Ffmpeg from 'fluent-ffmpeg'
|
|
2
|
+
import { readFile } from 'fs-extra'
|
|
3
|
+
import { tmpdir } from 'os'
|
|
4
|
+
|
|
5
|
+
const imagesToWebp = async (filename: string): Promise<Buffer> => {
|
|
6
|
+
const file = await new Promise<string>((resolve) => {
|
|
7
|
+
const name = `${tmpdir()}/${Math.random().toString(36)}.webp`
|
|
8
|
+
Ffmpeg(filename)
|
|
9
|
+
.outputOption('-lavfi split[v],palettegen,[v]paletteuse')
|
|
10
|
+
.outputOption('-vcodec libwebp')
|
|
11
|
+
.outputFPS(10)
|
|
12
|
+
.loop(0)
|
|
13
|
+
.save(name)
|
|
14
|
+
.on('end', () => resolve(name))
|
|
15
|
+
})
|
|
16
|
+
return await readFile(file)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default imagesToWebp
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import ffmpeg from 'fluent-ffmpeg'
|
|
2
|
+
import { writeFile, readFile, unlink } from 'fs-extra'
|
|
3
|
+
import { tmpdir } from 'os'
|
|
4
|
+
|
|
5
|
+
/** https://stackoverflow.com/questions/52156713/fluent-ffmpeg-h264-to-gif-throwing-error-1 */
|
|
6
|
+
const videoToGif = async (data: Buffer): Promise<Buffer> => {
|
|
7
|
+
const filename = `${tmpdir()}/${Math.random().toString(36)}`
|
|
8
|
+
const [video, gif] = ['video', 'gif'].map((ext) => `${filename}.${ext}`)
|
|
9
|
+
await writeFile(video, data)
|
|
10
|
+
await new Promise((resolve) => {
|
|
11
|
+
ffmpeg(video).save(gif).on('end', resolve)
|
|
12
|
+
})
|
|
13
|
+
const buffer = await readFile(gif)
|
|
14
|
+
;[video, gif].forEach((file) => unlink(file))
|
|
15
|
+
return buffer
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default videoToGif
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuu-reinzz/haruka-lib",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Library extra for bot WhatsApp",
|
|
5
5
|
"main": "main/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -15,8 +15,17 @@
|
|
|
15
15
|
":v"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"axios": "^1.12.1",
|
|
18
19
|
"better-sqlite3": "^12.5.0",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
20
|
+
"file-type": "^16.5.3",
|
|
21
|
+
"fluent-ffmpeg": "^2.1.3",
|
|
22
|
+
"fs-extra": "^11.2.0",
|
|
23
|
+
"node-fetch": "^3.3.2",
|
|
24
|
+
"node-webpmux": "^3.2.1",
|
|
25
|
+
"sharp": "^0.34.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^25.0.3",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
21
30
|
}
|
|
22
31
|
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"rootDir": "main/sticker-engine/type-script",
|
|
6
|
+
"outDir": "main/sticker-engine/java-script",
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"moduleResolution": "node"
|
|
11
|
+
},
|
|
12
|
+
"include": ["main/sticker-engine/type-script/**/*"]
|
|
13
|
+
}
|