diff --git a/Dockerfile b/Dockerfile index 9009800..64e5e15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,14 +5,13 @@ RUN pacman -Syu --noconfirm cmake python nodejs && \ sed -i 's/v3.21.0/v3.21.2/g' wcocr/CMakeLists.txt && \ mkdir -p wcocr/build && cd wcocr/build && cmake .. && make -j$(nproc) -FROM greyltc/archlinux-aur:yay AS builder +FROM greyltc/archlinux-aur:yay AS runner WORKDIR /app -RUN aur-install bun-bin +RUN aur-install bun-bin wechat-bin +COPY . . COPY --from=lib /src/wcocr/build/libwcocr.so . -# COPY src/index.ts . +RUN bun install --frozen-lockfile -# FROM greyltc/archlinux-aur:yay AS runner -# WORKDIR /app -# RUN aur-install wechat-bin -# COPY --from=builder /app/bun-bin . -# COPY --from=lib /src/wcocr/build/libwcocr.so . +EXPOSE 3000 + +CMD ["bun", "src/index.ts"] diff --git a/src/index.ts b/src/index.ts index 1c98e9c..7170e3b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,28 @@ import { Hono } from 'hono' +import { Buffer } from 'node:buffer' + +import { WechatOCRClient } from './wcocr' const app = new Hono() +const client = new WechatOCRClient() -app.get('/', c => c.text('Hello World')) +app.post('/ocr', async (c) => { + const formData = await c.req.formData() + const imageFile = formData.get('image') + + if (!imageFile || typeof imageFile === 'string') { + return c.json({ error: 'Image file is required' }, 400) + } + + const imageBuffer = Buffer.from(await imageFile.arrayBuffer()) + + try { + const result = await client.recognize(imageBuffer) + return c.json(JSON.parse(result)) + } catch (error) { + console.error('OCR processing error:', error) + return c.json({ error: 'Failed to process image' }, 500) + } +}) export default app diff --git a/src/wcocr.ts b/src/wcocr.ts index 19d4949..4893c40 100644 --- a/src/wcocr.ts +++ b/src/wcocr.ts @@ -2,10 +2,14 @@ import type { Pointer } from 'bun:ffi' import { CString, dlopen, FFIType, JSCallback, suffix } from 'bun:ffi' import { Buffer } from 'node:buffer' +import { randomBytes } from 'node:crypto' +import { rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' const libPath = `libwcocr.${suffix}` -const WechatOCR = dlopen(libPath, { +const WechatOCRFFI = dlopen(libPath, { wechat_ocr: { args: [ FFIType.cstring, // ocr_exe路径 @@ -21,58 +25,59 @@ const WechatOCR = dlopen(libPath, { }, }) -// 主函数 -async function callWechatOcr( - ocrExe: string, - wechatDir: string, - imgfn: string, -): Promise { - return new Promise((resolve, reject) => { - // 创建回调函数 - const callback = new JSCallback( - (argPtr: Pointer) => { - const arg = new CString(argPtr).toString() - resolve(arg) - callback.close() // 释放回调 - }, - { - args: [FFIType.ptr], - returns: FFIType.void, - }, - ) +export class WechatOCRClient { + private ocrExe: string + private wechatDir: string - // 调用wechat_ocr函数 - const result = WechatOCR.symbols.wechat_ocr( - Buffer.from(`${ocrExe}\0`), // 转换为C字符串 - Buffer.from(`${wechatDir}\0`), - Buffer.from(`${imgfn}\0`), - callback, // 直接传递JSCallback实例 - ) + constructor(options?: { ocrExe?: string, wechatDir?: string }) { + this.ocrExe = options?.ocrExe ?? '/opt/wechat/wxocr' + this.wechatDir = options?.wechatDir ?? '/opt/wechat' + } - if (!result) { - callback.close() - reject(new Error('OCR调用失败')) + private async _callWechatOcr(imgfn: string): Promise { + return new Promise((resolve, reject) => { + // 创建回调函数 + const callback = new JSCallback( + (argPtr: Pointer) => { + const arg = new CString(argPtr).toString() + resolve(arg) + callback.close() // 释放回调 + }, + { + args: [FFIType.ptr], + returns: FFIType.void, + }, + ) + + // 调用wechat_ocr函数 + const result = WechatOCRFFI.symbols.wechat_ocr( + Buffer.from(`${this.ocrExe}\0`), // 转换为C字符串 + Buffer.from(`${this.wechatDir}\0`), + Buffer.from(`${imgfn}\0`), + callback, // 直接传递JSCallback实例 + ) + + if (!result) { + callback.close() + reject(new Error('OCR call failed')) + } + }) + } + + public async recognize(image: Buffer): Promise { + const tempPath = join(tmpdir(), `wechat-ocr-temp-${randomBytes(16).toString('hex')}.png`) + await writeFile(tempPath, image) + + try { + const result = await this._callWechatOcr(tempPath) + return result + } finally { + await rm(tempPath, { force: true }) } - }) -} + } -async function main() { - const ocrExe = '/opt/wechat/wxocr' - const wechatDir = '/opt/wechat' - const imgfn = '/home/imbytecat/Pictures/Screenshots/Screenshot_07-Jul_10-00-20_9907.png' - - console.log('OCR开始...') - try { - const result = await callWechatOcr(ocrExe, wechatDir, imgfn) - console.log('OCR结果:', result) - } catch (error) { - console.error('OCR错误:', error) - } finally { - // 清理资源 - WechatOCR.symbols.stop_ocr() - console.log('OCR结束...') + public close() { + WechatOCRFFI.symbols.stop_ocr() + console.log('OCR resources cleaned up.') } } - -// 运行主函数 -main()