Compare commits
29 Commits
api-suppor
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
aa9955d432 | ||
![]() |
7634f61cb9 | ||
![]() |
ff2d2bf290 | ||
![]() |
824506d968 | ||
![]() |
27aa5e9563 | ||
![]() |
95d8f03c48 | ||
![]() |
b16e2aa34a | ||
![]() |
57e5a62ca7 | ||
![]() |
0f5a1dd3ce | ||
![]() |
4985cd8ddd | ||
![]() |
82c3699e38 | ||
![]() |
e66d2f91a6 | ||
![]() |
2a56afaaf5 | ||
![]() |
77be3dfafe | ||
![]() |
ec1d399736 | ||
![]() |
4543258c1f | ||
![]() |
9ce7d47b8a | ||
![]() |
69629b1eb3 | ||
![]() |
09526242ff | ||
![]() |
d2b6d7bd3f | ||
![]() |
2637226c23 | ||
![]() |
f638c5d65e | ||
![]() |
6bd52ea74a | ||
![]() |
4e55b325e0 | ||
![]() |
2f8f57ade7 | ||
![]() |
ad532bd994 | ||
![]() |
9c6745fcf5 | ||
![]() |
d7e0bf4106 | ||
![]() |
bee8754e24 |
129
README.md
129
README.md
@ -1,12 +1,31 @@
|
||||
# 🖼️ QwenLM OCR
|
||||
<div align="center"> <img src="https://github.com/Cunninger/ocr-based-qwen/blob/main/ocr.png?raw=true" width="120"> </div>
|
||||
|
||||
本项目是对 [QwenLM](https://chat.qwenlm.ai/) 的 OCR 功能进行逆向工程的实现。通过调用 QwenLM 的 API,你可以从图片中提取文字内容,并且该项目支持一键部署到 **Cloudflare Workers** (CF) 上。
|
||||
|
||||
# QwenLM OCR
|
||||
|
||||
|
||||
本项目基于 [QwenLM](https://chat.qwenlm.ai/) 。通过调用 QwenLM 的 ”API“,你可以从图片中提取文字内容,并且该项目支持一键部署到 **Cloudflare Workers** (CF) 上。
|
||||
|
||||
## 项目展示
|
||||

|
||||

|
||||
|
||||
## 测试cookie
|
||||
- 如果出现**处理失败: 文件上传失败的错误**,说明测试Cookie 上传文件过多, 尝试获取自己账号的Cookie 使用
|
||||
```
|
||||
{
|
||||
"code": "RateLimited",
|
||||
"detail": "Reached file upload limited: too many files uploaded in (86400.0) seconds."
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
- cookie1:
|
||||
```txt
|
||||
cna=LI8HIAirQyACAWrg3cNOjH8F; _gcl_au=1.1.1200556497.1736728490; xlly_s=1; acw_tc=c2b96feb4d2929a1649ea96dc00590956477696933f61783d23e6c95429ecf74; x-ap=ap-southeast-1; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTc0MTE0ODM0MX0.-2hF4l1iJf8r5U6UzoXyc-TFqx0i5luWmtJk0kk8T5o; ssxmod_itna=Yq+hqIxjx=AKi7eitD8IDCWBfbxxl4BtGRDeq7tDRDFqApxDHmLWxoDUxxu2hE80DD0ADnG3BUxGN4tDxiNDAg40iDC4mL37ezTxthtQQhgDCTYCu2rpTCGGE7P3rnTE0M25BA6w3KxxGLDY=DCqhxl4eD4R3Dt4DIDAYDDxDWFeGIDGYD7hb=ymbDm44aYxGy3nbDA3TDbhb5xitYDDUAKeG2igbhDDNheFY49GG7yC0OorHDAqh==GbDjqPD/RxLP+bkXtkh/CTA=BapCeaWxBQD7we3xYE4c=IdqZawfiEwDELx0De/iGKGxdYmPlvzYKGlpVGGoWDqjqQlXnlvnnPDDc=Y9=buiDrP3KYtvS85lmHQDxPoGb0rz0THimDgri0i8B0=nxNCDtYDb30eDBeIG4/g8BvDD; ssxmod_itna2=Yq+hqIxjx=AKi7eitD8IDCWBfbxxl4BtGRDeq7tDRDFqApxDHmLWxoDUxxu2hE80DD0ADnG3BDxDfk7K7e5xDLiARWCTe9A4D/9o1+WeqlSGSWe4dTPhBUq4kzjyE41MPvWM=BtDwWHWiQ4B+GVChGZYlDDoA6gCj2VGnQeqqxeBSiqeT69Cg2Q6qrEGqv/DTQR8MRo3m3E7Gd7mf42Re2lYpNic=beSRp3tCDgWT7khOXGqf2lBpre5BtExlRw6cUwWwDxNSFUl/8Un/RGZz8/jLk2t1XZC79Eb7iRTPTMPn5MTHW2fs1DlsH0z9RxCLW1Rdb2QFhd9biugr+2aTW1oeYAbtORQNMW43+m2Gcb=TuFtQGqhiTqrNfdwSOe8WHSWTr7cRaw833Yzw31d4iN+Q9OO7izUj1uSdYrh4dIvx5CwYD45Vji4EbkiaNQQGbt1hDa8Ymmda3r3lThChY8Nm2IscznS+mS43izuLzUYN94syC3tQi03=u3Q6az931moHSOLY3OFPKoMjqMUDbtebxw=rT2rBAv08uRkWHzFtGp2lUSSb3z41pTZcx/8NB3jD+XnIpLFEw8bv4r58wRLLwubFTQ9cPDv+9k36tvoCYXlso4qGA44E3y737YNBDmUct2Iahxq76iTrlGotG1NBBN=Tx4hkA2Zp887gfc4Tp8V1wGCf49XeA=0Xu3uIKqYQADh0ABmqlQ4w=YVlDPjO9KH2xaG3C8ID5DeY41AxMxR2NIGhSD+5q0UQGx0Ue4dAi0DwbY+f7n1Gwz014LQ2DY+42xI2zG5TmhfDtXehC40aWxfDi0xIGNQ3gGkYeu2fAfADwY31mkAooDxD; SERVERID=da7472215188c88fe194f138f1242089|1738557149|1738556339; SERVERCORSID=da7472215188c88fe194f138f1242089|1738557149|1738556339; isg=BBcXHLltPM7zv7hi-FnT0HW6pothXOu-LIccEmlG4eZGmDrabS7mDq_--ziGPsM2; tfstk=gxYniE986HSQfEpYpd7Icypow5otjW_5cLUReaBrbOW6JLnCehbygKjRyBdPr4XN8gtde9BlrQ9m6x3xkBOCPgkxHqIXCrTVlurzY8QNb_blTz_06LRCPaktd3lv5B9qU3M64alG7_fz8aWFTPlGZsXPz9SzQl5VQT7yU9SaQ617YJzUzRlGNOWPzaJP7jQ8r9CV1FkgVOPK4CrD_6jh3BWH9KLadGrBiO4zzFjfxtuAIzzyS6Ot3kbuo0B2c9IACduT-aANZKfDKRkGzCYBoG8ECzvDIn7G1hMuqtxy9ntPjXueswXhmFI0Zk7H4pTlAHVSGC7eCn6fYcM1seLAqtsgQvR9s9SDqpH_z9KD_djvWRUA7I-GSgozbo5IVz1ZwFr7VM51stK4FEPS3FZLvfcg0MIF16r-sfq7VM51sthisoJlY11U2
|
||||
cna=pklJII7LlmACAWrjg1e7Aw5T; _bl_uid=vjmdq8ej4XLdptzy8g22cbqap10w; _gcl_au=1.1.2061498123.1740790694.1095090244.1742178801.1742178800; visitor_id=69d9708501480ef09203b791ac6bf725; x-ap=ap-southeast-1; xlly_s=1; acw_tc=0a03e53417425630262545566e597ceb582665688952129fe15ad53c13a1f9; ssxmod_itna=eq0xcD9DgDnDRQGG0K3qq7K7KqKqeTx0dGMD3Mq7tDRDFqAP5DHIOFP5DUgork7Dhx=80hqDsqQY4GzDiMPGhDBmAHQYDDKCYRhc0NPY1mT2DC1uhedHmd4N7ArjRr9ZZSrtexB3DEx0==nTirDYYEDBYD74G+DDeDir3Dj4GmDGA3deDFCmbQcnre=xDwDB=DmqG2Q2rDm4DfDDd5O7=jCWroDDtDAun1PPDADAfY24Dl08OjCGDK24GWClR/iceaMawdV2rDjdPD/RG021H2o5LtXCMs+9Ye=cWDtqD986RVWaIYVQu4wIYCDqQRDPhFCIYQDblxx8DFnD47DtGA4YiiGGbjaHCGKR+63+FQwFe9x=EF7Dh+5ZUvwRy/exxfRDAuCjEYeeCtGqCIOlgtCiN/riQGQ=G4WGeuricqFeD; ssxmod_itna2=eq0xcD9DgDnDRQGG0K3qq7K7KqKqeTx0dGMD3Mq7tDRDFqAP5DHIOFP5DUgork7Dhx=80o4DW+DDwYpQkUCgDCMCnMHiqezU=ED; SERVERID=7f8d8d352ef7abbfd818d506cbe1b627|1742563117|1742563026; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTc0NTE1NTExN30.4g_Jz3Gv_OmBRMSJVoy641H9iCG90v39SK18wu9mCFQ; tfstk=ga7qs-x4urU2V1_4IQYZ4vDR3xTvJE2I0N96s1fMGKv0HNTwsL5Zhi11sFzwEExOcd9XQF5OL7wQRy1AMFpgdJaCSmqqdFpg5FqWr_hjUk2QRy1cFK8BDJ_jwASyHCYMihAmq7R6tFADndVyECRKjxXMS7PysCHMid0Mr3ApEdYMSOfuaCMKRtBtdBx0_VcIk0DvMnJhiLuNMaRD0mCDUV0Gzt-2K29rSVbyM_MBBrgUfpjphHtPEzg27_ANhL63-48kYMC6_tzqoEsl2ZLfPJlp0OJyxatzrYRPz6sHw_rUa_7w3HbD4fgXoERGIhS8L2O2lg-FuiFtEsXB3MYA1XyfaHSyvQxo_mYfA6_9xZ4qpLtp_t95Ky0yrhjzLAdugA_O0AmwmQdyd7Pr8_lRpTUzeHmtX3xHapNvjcn9mQdyd7PrXcKx-QJQMhf..; isg=BA0NXKJIVjFc4PIC5ORdQdZ0HCmH6kG8OYRCyE-SSaQTRi34FzpRjFvAstogsll0
|
||||
```
|
||||
- cookie2:
|
||||
```txt
|
||||
acw_tc=c2db8f77aa0023a996777b8537356b308a98c7fb128b3020323ddd0d440fee2e; x-ap=ap-southeast-1; _gcl_au=1.1.485014960.1742563291; cna=2lVkIP9JX0cCAWrgu3Se+w7K; _bl_uid=L2mdt8wtidFtCg6hOwepuqdbswp9; xlly_s=1; ssxmod_itna=eqfx9DRDgDuCq4e9irD+x0xiIt4Kk0q0dGMD3u47t=GcD8xx0POD8ne5DUhKgGg5hxPY0hqDsPexiNDAg40iDCbmd87DDqtqgUb7DPmYRE7DtUgiz1dRuKOGrh2LROs=/Dc3eOieGLDmKDU2Dre34DxPPD5xDTDWeDGDD3rxGaDmeDe1ghD04mFfovifAoD7eDXxGCz74mDYPDWxDFibw8HxowrDDCDi5flb3DixiaTzPDB1PuHhDKtzPDEDOIVEfozBdqO/PmD7H3DlPx+vdXwH/asIoyso6r3+3fDCKDjgSpxSrQTKSI5b8+Dxn0YeWeOgO+GDYhxqDty+xKGKBB3Qix70DeDxoxxQGKgGw9xqQAYOEQOO7YCpOhKKbzcqsibslTe7G3qGtuOv+GiqB=klOpOAMA5KB5e35K+D3ixTQmkeh4D; ssxmod_itna2=eqfx9DRDgDuCq4e9irD+x0xiIt4Kk0q0dGMD3u47t=GcD8xx0POD8ne5DUhKgGg5hxPY0o4DW+DDwYpQkUCgDCMSOB5qeg0/mkbxD; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImQ5MWIwY2ExLTlhZmMtNDkwNi05MTQ4LWNlN2M4ZjZhN2NmYyIsImV4cCI6MTc0NTE1NTMzM30.jqUN03CY_bB6GniWCn6ISQkdnpN59hm3Gug655Dd2GI; SERVERID=4c8b4dba95825d8ac7605fad188b7907|1742563334|1742563289; tfstk=gw6qcdO43-eqJr64S3vZa2mpW7vvhK4QnOT6jGjMcEY0kO9wjUSZlss1jdywrNLchdDirF7wXqADIjvZQUth1qDcmC8kjNO0DnVvQbx6fxcMISLZDT7ECxaYMGDwCd4QRJwCDgJ9Iy_g58eDkhIkSuO0iuwfud4QR8tK5z0vIZNRCkiPqUKwjjDcS3mkyhkisNYDZ4xevdYGSN0lqntsiAcDSYqyXUYMIdbMEvFVkN3BxIqZIcF3PztMgejDzASAUnAqMiYrIAXPuI8ndUkiIT-VVmR-AvP6-6LXOQ7uFxJNqh7XPw2nULAF9GtNu-kDh_fRHK678DdV7URhFQyoz3WPA6xR4WrP4O82ttArfx9D3hbM__UEdnJfaBWVMl3lmwTVtKIsYrsyT_RpqQmZiK1CAOdhn-upPBKNlU_3KYJksgkKW3qq5OCqSfRD238Q4u7onRcc_mCrKfh9ZBxyRodi6fdD238Q4ult6IHH4eZvs; isg=BLGxZSYScp1XxN6JZK3ED2FvwD1LniUQRPVRw5PFbniWutAM2e5m4VbM3k7cRr1I
|
||||
```
|
||||
## 🚀 功能特性
|
||||
|
||||
@ -17,8 +36,30 @@ cna=LI8HIAirQyACAWrg3cNOjH8F; _gcl_au=1.1.1200556497.1736728490; xlly_s=1; acw_t
|
||||
- **历史记录**:保存每次识别的结果和图片,方便查看。
|
||||
- **一键复制**:轻松复制识别结果到剪贴板。
|
||||
- **数学公式识别**:特别优化了对数学公式的提取,支持 LaTeX 格式输出。
|
||||
- **API 支持**:提供 `curl` 接口调用,支持 base64 和图片 URL 两种方式。
|
||||
- **API 支持**:提供 `curl` 接口调用,支持通过图片文件、base64 和图片 URL 3种方式。(Apifox调用文档示例(**仅作为代码示例,这个网页调试有问题**):https://0vkh6v4ad8.apifox.cn/)
|
||||
- **验证码识别**:新增验证码识别功能,支持常见类型的验证码(如数字、字母、混合字符等),提升自动化处理能力。
|
||||
- **自定义prompt**: 在高级模式下(v1.1.0支持),用户可以自定义 prompt,跳过格式化处理,直接返回原始结果,而在普通模式下,使用默认的 prompt 并保持现有的格式化处理逻辑。
|
||||
## qwen模型接口:
|
||||
https://chat.qwenlm.ai/api/models
|
||||
## 提示词工程
|
||||
```
|
||||
const defaultPrompt =
|
||||
'不要输出任何额外的解释或说明,禁止输出例如:识别内容、以上内容已严格按照要求进行格式化和转换等相关无意义的文字!' + '请识别图片中的内容,注意以下要求:\n' +
|
||||
'对于数学公式和普通文本:\n' +
|
||||
'1. 所有数学公式和数学符号都必须使用标准的LaTeX格式\n' +
|
||||
'2. 行内公式使用单个$符号包裹,如:$x^2$\n' +
|
||||
'3. 独立公式块使用两个$$符号包裹,如:$$\\sum_{i=1}^n i^2$$\n' +
|
||||
'4. 普通文本保持原样,不要使用LaTeX格式\n' +
|
||||
'5. 保持原文的段落格式和换行\n' +
|
||||
'6. 明显的换行使用\\n表示\n' +
|
||||
'7. 确保所有数学符号都被正确包裹在$或$$中\n\n' +
|
||||
'对于验证码图片:\n' +
|
||||
'1. 只输出验证码字符,不要加任何额外解释\n' +
|
||||
'2. 忽略干扰线和噪点\n' +
|
||||
'3. 注意区分相似字符,如0和O、1和l、2和Z等\n' +
|
||||
'4. 验证码通常为4-6位字母数字组合\n\n' +
|
||||
'';
|
||||
```
|
||||
## 🛠️ 部署指南
|
||||
|
||||
### 1. 部署到 Cloudflare Workers
|
||||
@ -57,20 +98,22 @@ cna=LI8HIAirQyACAWrg3cNOjH8F; _gcl_au=1.1.1200556497.1736728490; xlly_s=1; acw_t
|
||||
5. **API 调用**:
|
||||
- **支持 base64**:
|
||||
```bash
|
||||
curl --location 'https://ocr.doublefenzhuan.me/api/recognize/base64' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
|
||||
"base64Image": "xxx"
|
||||
}'
|
||||
curl -X POST \
|
||||
'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/base64' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'x-custom-cookie: YOUR_COOKIE_STRING' \
|
||||
-d '{
|
||||
"base64Image": "YOUR_BASE64_IMAGE_STRING"
|
||||
}'
|
||||
```
|
||||
- **支持图片 URL**:
|
||||
```bash
|
||||
curl --location 'https://ocr.doublefenzhuan.me/api/recognize/url' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
|
||||
"imageUrl": "xxxx"
|
||||
curl -X POST \
|
||||
'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/url' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'x-custom-cookie: YOUR_COOKIE_STRING' \
|
||||
-d '{
|
||||
"imageUrl": "YOUR_IMAGE_URL"
|
||||
}'
|
||||
```
|
||||
6. **验证码识别**
|
||||
@ -115,31 +158,51 @@ cna=LI8HIAirQyACAWrg3cNOjH8F; _gcl_au=1.1.1200556497.1736728490; xlly_s=1; acw_t
|
||||
|
||||
### 2025/01/13 18点34分 支持`curl`接口调用
|
||||
- **支持base64**:
|
||||
```
|
||||
curl --location 'https://ocr.doublefenzhuan.me/api/recognize/base64' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
|
||||
"base64Image": "xxx"
|
||||
}'
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/base64' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'x-custom-cookie: YOUR_COOKIE_STRING' \
|
||||
-d '{
|
||||
"base64Image": "YOUR_BASE64_IMAGE_STRING"
|
||||
}'
|
||||
```
|
||||
- 效果图:
|
||||

|
||||

|
||||
|
||||
- **支持图片URL**:
|
||||
```bash
|
||||
curl --location 'https://ocr.doublefenzhuan.me/api/recognize/url' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
|
||||
|
||||
"imageUrl": "xxxx"
|
||||
}'
|
||||
curl -X POST \
|
||||
'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/url' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'x-custom-cookie: YOUR_COOKIE_STRING' \
|
||||
-d '{
|
||||
"imageUrl": "YOUR_IMAGE_URL"
|
||||
}'
|
||||
```
|
||||
- 效果图:
|
||||

|
||||

|
||||
|
||||
- **通过图片文件识别(需要先上传获取imageId)**
|
||||
```bash
|
||||
# 1. 先上传文件
|
||||
curl -X POST \
|
||||
'https://test-qwen-cor.aughumes8.workers.dev/proxy/upload' \
|
||||
-H 'x-custom-cookie: YOUR_COOKIE_STRING' \
|
||||
-F 'file=@/path/to/your/image.jpg'
|
||||
|
||||
# 2. 使用返回的imageId进行识别
|
||||
curl -X POST \
|
||||
'https://test-qwen-cor.aughumes8.workers.dev/recognize' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'x-custom-cookie: YOUR_COOKIE_STRING' \
|
||||
-d '{
|
||||
"imageId": "RETURNED_IMAGE_ID"
|
||||
}'
|
||||
```
|
||||
## Cloudflare访问数据
|
||||

|
||||
|
||||
## 致谢
|
||||

|
||||
## 趋势
|
||||
[](https://star-history.com/#Cunninger/ocr-based-qwen&Date)
|
||||
|
603
worker.js
603
worker.js
@ -60,17 +60,30 @@ async function handleRequest(request) {
|
||||
// 处理图片URL识别
|
||||
async function handleImageUrlRecognition(request) {
|
||||
try {
|
||||
const { token, imageUrl } = await request.json();
|
||||
const { imageUrl } = await request.json();
|
||||
const cookie = request.headers.get('x-custom-cookie');
|
||||
|
||||
if (!token || !imageUrl) {
|
||||
if (!cookie || !imageUrl) {
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Missing token or imageUrl'
|
||||
error: 'Missing cookie or imageUrl'
|
||||
}), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
// 从cookie中提取token
|
||||
const tokenMatch = cookie.match(/token=([^;]+)/);
|
||||
if (!tokenMatch) {
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Invalid cookie format: missing token'
|
||||
}), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
const token = tokenMatch[1];
|
||||
|
||||
// 下载图片
|
||||
const imageResponse = await fetch(imageUrl);
|
||||
const imageBlob = await imageResponse.blob();
|
||||
@ -84,6 +97,7 @@ async function handleImageUrlRecognition(request) {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'authorization': `Bearer ${token}`,
|
||||
'cookie': cookie
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
@ -91,8 +105,8 @@ async function handleImageUrlRecognition(request) {
|
||||
const uploadData = await uploadResponse.json();
|
||||
if (!uploadData.id) throw new Error('File upload failed');
|
||||
|
||||
// 调用识别API
|
||||
return await recognizeImage(token, uploadData.id);
|
||||
// 调用通用识别函数
|
||||
return await recognizeImage(token, uploadData.id, request);
|
||||
} catch (error) {
|
||||
return new Response(JSON.stringify({
|
||||
error: error.message || 'Internal Server Error'
|
||||
@ -106,22 +120,32 @@ async function handleImageUrlRecognition(request) {
|
||||
// 处理Base64识别
|
||||
async function handleBase64Recognition(request) {
|
||||
try {
|
||||
const { token, base64Image } = await request.json();
|
||||
const { base64Image } = await request.json();
|
||||
const cookie = request.headers.get('x-custom-cookie');
|
||||
|
||||
if (!token || !base64Image) {
|
||||
if (!cookie || !base64Image) {
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Missing token or base64Image'
|
||||
error: 'Missing cookie or base64Image'
|
||||
}), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
// 转换Base64为Blob
|
||||
const imageData = base64Image.startsWith('data:') ?
|
||||
base64Image :
|
||||
'data:image/png;base64,' + base64Image;
|
||||
// 从cookie中提取token
|
||||
const tokenMatch = cookie.match(/token=([^;]+)/);
|
||||
if (!tokenMatch) {
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Invalid cookie format: missing token'
|
||||
}), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
const token = tokenMatch[1];
|
||||
|
||||
// 转换Base64为Blob
|
||||
const imageData = base64Image.startsWith('data:') ? base64Image : 'data:image/png;base64,' + base64Image;
|
||||
const response = await fetch(imageData);
|
||||
const blob = await response.blob();
|
||||
|
||||
@ -134,6 +158,7 @@ async function handleBase64Recognition(request) {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'authorization': `Bearer ${token}`,
|
||||
'cookie': cookie
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
@ -141,8 +166,8 @@ async function handleBase64Recognition(request) {
|
||||
const uploadData = await uploadResponse.json();
|
||||
if (!uploadData.id) throw new Error('File upload failed');
|
||||
|
||||
// 调用识别API
|
||||
return await recognizeImage(token, uploadData.id);
|
||||
// 调用通用识别函数
|
||||
return await recognizeImage(token, uploadData.id, request);
|
||||
} catch (error) {
|
||||
return new Response(JSON.stringify({
|
||||
error: error.message || 'Internal Server Error'
|
||||
@ -156,18 +181,23 @@ async function handleBase64Recognition(request) {
|
||||
// 处理文件识别 (原有功能)
|
||||
async function handleFileRecognition(request) {
|
||||
try {
|
||||
const { token, imageId } = await request.json();
|
||||
const { imageId } = await request.json();
|
||||
const cookie = request.headers.get('x-custom-cookie') || '';
|
||||
|
||||
if (!token || !imageId) {
|
||||
if (!cookie || !imageId) {
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Missing token or imageId'
|
||||
error: 'Missing cookie or imageId'
|
||||
}), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
return await recognizeImage(token, imageId, request); // 传递 request 对象
|
||||
// 从cookie中提取token
|
||||
const tokenMatch = cookie.match(/token=([^;]+)/);
|
||||
const token = tokenMatch ? tokenMatch[1] : '';
|
||||
|
||||
return await recognizeImage(token, imageId, request);
|
||||
} catch (error) {
|
||||
return new Response(JSON.stringify({
|
||||
error: error.message || 'Internal Server Error'
|
||||
@ -182,19 +212,24 @@ async function handleFileRecognition(request) {
|
||||
async function handleProxyUpload(request) {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
const token = request.headers.get('Authorization').replace('Bearer ', '');
|
||||
|
||||
const cookie = request.headers.get('x-custom-cookie') || '';
|
||||
|
||||
// 从cookie中提取token
|
||||
const tokenMatch = cookie.match(/token=([^;]+)/);
|
||||
const token = tokenMatch ? tokenMatch[1] : '';
|
||||
|
||||
const response = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'authorization': `Bearer ${token}`,
|
||||
'cookie': cookie
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
return new Response(JSON.stringify(data), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -216,45 +251,62 @@ async function handleProxyUpload(request) {
|
||||
|
||||
// 通用的识别函数
|
||||
async function recognizeImage(token, imageId, request) {
|
||||
// 从请求头获取 cookie
|
||||
const cookie = request.headers.get('x-custom-cookie') || '';
|
||||
|
||||
// 从请求头中获取高级模式状态和自定义prompt
|
||||
const advancedMode = request.headers.get('x-advanced-mode') === 'true';
|
||||
|
||||
// 解码自定义prompt
|
||||
let customPrompt = '';
|
||||
try {
|
||||
const encodedPrompt = request.headers.get('x-custom-prompt');
|
||||
if (encodedPrompt) {
|
||||
customPrompt = decodeURIComponent(atob(encodedPrompt));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Prompt解码错误:', error);
|
||||
}
|
||||
|
||||
const defaultPrompt =
|
||||
'不要输出任何额外的解释或说明,禁止输出例如:识别内容、以上内容已严格按照要求进行格式化和转换等相关无意义的文字!' + '请识别图片中的内容,注意以下要求:\n' +
|
||||
'对于数学公式和普通文本:\n' +
|
||||
'1. 所有数学公式和数学符号都必须使用标准的LaTeX格式\n' +
|
||||
'2. 行内公式使用单个$符号包裹,如:$x^2$\n' +
|
||||
'3. 独立公式块使用两个$$符号包裹,如:$$\\sum_{i=1}^n i^2$$\n' +
|
||||
'4. 普通文本保持原样,不要使用LaTeX格式\n' +
|
||||
'5. 保持原文的段落格式和换行\n' +
|
||||
'6. 明显的换行使用\\n表示\n' +
|
||||
'7. 确保所有数学符号都被正确包裹在$或$$中\n\n' +
|
||||
'对于验证码图片:\n' +
|
||||
'1. 只输出验证码字符,不要加任何额外解释\n' +
|
||||
'2. 忽略干扰线和噪点\n' +
|
||||
'3. 注意区分相似字符,如0和O、1和l、2和Z等\n' +
|
||||
'4. 验证码通常为4-6位字母数字组合\n\n' +
|
||||
'';
|
||||
const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'PostmanRuntime/7.43.0',
|
||||
'User-Agent': 'PostmanRuntime/7.43.0',
|
||||
'accept': '*/*',
|
||||
'authorization': `Bearer ${token}`,
|
||||
'cookie': request.headers.get('x-custom-cookie') || '', // 使用自定义请求头中的 cookie
|
||||
'cookie': cookie,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
stream: false,
|
||||
chat_type: "t2t",
|
||||
model: 'qwen2.5-vl-72b-instruct',
|
||||
model: 'qwen-max-latest',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: '请识别图片中的内容,注意以下要求:\n' +
|
||||
'对于数学公式和普通文本:\n' +
|
||||
'1. 所有数学公式和数学符号都必须使用标准的LaTeX格式\n' +
|
||||
'2. 行内公式使用单个$符号包裹,如:$x^2$\n' +
|
||||
'3. 独立公式块使用两个$$符号包裹,如:$$\\sum_{i=1}^n i^2$$\n' +
|
||||
'4. 普通文本保持原样,不要使用LaTeX格式\n' +
|
||||
'5. 保持原文的段落格式和换行\n' +
|
||||
'6. 明显的换行使用\\n表示\n' +
|
||||
'7. 确保所有数学符号都被正确包裹在$或$$中\n\n' +
|
||||
'对于验证码图片:\n' +
|
||||
'1. 只输出验证码字符,不要加任何额外解释\n' +
|
||||
'2. 忽略干扰线和噪点\n' +
|
||||
'3. 注意区分相似字符,如0和O、1和l、2和Z等\n' +
|
||||
'4. 验证码通常为4-6位字母数字组合\n\n' +
|
||||
'不要输出任何额外的解释或说明',
|
||||
text: advancedMode ? customPrompt : defaultPrompt,
|
||||
chat_type: "t2t"
|
||||
},
|
||||
{
|
||||
type: 'image',
|
||||
{
|
||||
type: 'image',
|
||||
image: imageId,
|
||||
chat_type: "t2t"
|
||||
},
|
||||
@ -270,30 +322,33 @@ async function recognizeImage(token, imageId, request) {
|
||||
const data = await response.json();
|
||||
let result = data.choices[0]?.message?.content || '识别失败';
|
||||
|
||||
// 如果结果长度小于10且只包含字母数字,很可能是验证码
|
||||
if (result.length <= 10 && /^[A-Za-z0-9]+$/.test(result)) {
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
result: result.toUpperCase(), // 验证码统一转大写
|
||||
type: 'captcha'
|
||||
}), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
}
|
||||
// 只在非高级模式下进行格式化处理
|
||||
if (!advancedMode) {
|
||||
// 如果结果长度小于10且只包含字母数字,很可能是验证码
|
||||
if (result.length <= 10 && /^[A-Za-z0-9]+$/.test(result)) {
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
result: result.toUpperCase(),
|
||||
type: 'captcha'
|
||||
}), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 其他情况(数学公式和普通文本)的处理
|
||||
result = result
|
||||
.replace(/\\(/g, '\\(')
|
||||
.replace(/\\)/g, '\\)')
|
||||
.replace(/\n{3,}/g, '\n\n')
|
||||
.replace(/([^\n])\n([^\n])/g, '$1\n$2')
|
||||
.replace(/\$\s+/g, '$')
|
||||
.replace(/\s+\$/g, '$')
|
||||
.replace(/\$\$/g, '$$')
|
||||
.trim();
|
||||
// 其他情况(数学公式和普通文本)的处理
|
||||
result = result
|
||||
.replace(/\\(/g, '\\(')
|
||||
.replace(/\\)/g, '\\)')
|
||||
.replace(/\n{3,}/g, '\n\n')
|
||||
.replace(/([^\n])\n([^\n])/g, '$1\n$2')
|
||||
.replace(/\$\s+/g, '$')
|
||||
.replace(/\s+\$/g, '$')
|
||||
.replace(/\$\$/g, '$$')
|
||||
.trim();
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
@ -1015,139 +1070,215 @@ function getHTML() {
|
||||
' background: rgba(255, 255, 255, 0.9);',
|
||||
' backdrop-filter: blur(5px);',
|
||||
' z-index: 900;',
|
||||
' border-top: 1px solid rgba(0, 0, 0, 0.1);',
|
||||
' display: flex;',
|
||||
' justify-content: center;',
|
||||
' align-items: center;',
|
||||
' display: flex;',
|
||||
' justify-content: center;',
|
||||
' align-items: center;',
|
||||
' }',
|
||||
' .footer-content {',
|
||||
' text-align: center;',
|
||||
' width: 100%;',
|
||||
' }',
|
||||
' .footer-content {',
|
||||
' text-align: center;',
|
||||
' width: 100%;',
|
||||
' }',
|
||||
' .powered-by {',
|
||||
' color: #7f8c8d;',
|
||||
' font-size: 0.9rem;',
|
||||
' }',
|
||||
' color: #7f8c8d;',
|
||||
' font-size: 0.9rem;',
|
||||
' }',
|
||||
|
||||
' .powered-by a {',
|
||||
' color: #3498db;',
|
||||
' text-decoration: none;',
|
||||
' transition: color 0.3s ease;',
|
||||
' font-weight: 500;',
|
||||
' }',
|
||||
' .powered-by a {',
|
||||
' color: #3498db;',
|
||||
' text-decoration: none;',
|
||||
' transition: color 0.3s ease;',
|
||||
' font-weight: 500;',
|
||||
' }',
|
||||
|
||||
' .powered-by a:hover {',
|
||||
' color: #2980b9;',
|
||||
' }',
|
||||
' .powered-by a:hover {',
|
||||
' color: #2980b9;',
|
||||
' }',
|
||||
|
||||
' .powered-by {',
|
||||
' color: #7f8c8d;',
|
||||
' font-size: 0.9rem;',
|
||||
' }',
|
||||
' .powered-by {',
|
||||
' color: #7f8c8d;',
|
||||
' font-size: 0.9rem;',
|
||||
' }',
|
||||
|
||||
' .powered-by a {',
|
||||
' color: #3498db;',
|
||||
' text-decoration: none;',
|
||||
' transition: color 0.3s ease;',
|
||||
' font-weight: 500;',
|
||||
' }',
|
||||
' .powered-by a {',
|
||||
' color: #3498db;',
|
||||
' text-decoration: none;',
|
||||
' transition: color 0.3s ease;',
|
||||
' font-weight: 500;',
|
||||
' }',
|
||||
|
||||
' .powered-by a:hover {',
|
||||
' color: #2980b9;',
|
||||
' }',
|
||||
' .powered-by a:hover {',
|
||||
' color: #2980b9;',
|
||||
' }',
|
||||
|
||||
' /* 输入控件组样式 */',
|
||||
' .input-controls {',
|
||||
' margin-top: 15px;',
|
||||
' width: 100%;',
|
||||
' }',
|
||||
' /* 输入控件组样式 */',
|
||||
' .input-controls {',
|
||||
' margin-top: 15px;',
|
||||
' width: 100%;',
|
||||
' }',
|
||||
|
||||
' .button-group {',
|
||||
' display: flex;',
|
||||
' gap: 10px;',
|
||||
' margin-top: 10px;',
|
||||
' justify-content: center;',
|
||||
' }',
|
||||
' .button-group {',
|
||||
' display: flex;',
|
||||
' gap: 10px;',
|
||||
' margin-top: 10px;',
|
||||
' justify-content: center;',
|
||||
' }',
|
||||
|
||||
' #urlInput {',
|
||||
' width: 100%;',
|
||||
' padding: 10px;',
|
||||
' border: 1px solid #dcdde1;',
|
||||
' border-radius: 8px;',
|
||||
' resize: none;',
|
||||
' font-size: 14px;',
|
||||
' }',
|
||||
' #urlInput {',
|
||||
' width: 100%;',
|
||||
' padding: 10px;',
|
||||
' border: 1px solid #dcdde1;',
|
||||
' border-radius: 8px;',
|
||||
' resize: none;',
|
||||
' font-size: 14px;',
|
||||
' }',
|
||||
|
||||
' .github-link {',
|
||||
' position: fixed;',
|
||||
' right: 150px;', // 放在 token 设置按钮左边',
|
||||
' top: 20px;',
|
||||
' background: #333;', // GitHub 的深色背景',
|
||||
' color: white;',
|
||||
' border: none;',
|
||||
' padding: 10px;',
|
||||
' border-radius: 50%;', // 圆形按钮',
|
||||
' cursor: pointer;',
|
||||
' z-index: 1001;',
|
||||
' width: 40px;',
|
||||
' height: 40px;',
|
||||
' display: flex;',
|
||||
' align-items: center;',
|
||||
' justify-content: center;',
|
||||
' transition: background 0.3s ease;',
|
||||
' }',
|
||||
' .github-link {',
|
||||
' position: fixed;',
|
||||
' right: 150px;',
|
||||
' top: 20px;',
|
||||
' background: #333;',
|
||||
' color: white;',
|
||||
' border: none;',
|
||||
' padding: 10px;',
|
||||
' border-radius: 50%;',
|
||||
' cursor: pointer;',
|
||||
' z-index: 1001;',
|
||||
' width: 40px;',
|
||||
' height: 40px;',
|
||||
' display: flex;',
|
||||
' align-items: center;',
|
||||
' justify-content: center;',
|
||||
' transition: background 0.3s ease;',
|
||||
' }',
|
||||
|
||||
' .github-link:hover {',
|
||||
' background: #24292e;', // GitHub hover 颜色',
|
||||
' }',
|
||||
' .github-link:hover {',
|
||||
' background: #24292e;',
|
||||
' }',
|
||||
|
||||
' .github-icon {',
|
||||
' width: 24px;',
|
||||
' height: 24px;',
|
||||
' }',
|
||||
' .github-icon {',
|
||||
' width: 24px;',
|
||||
' height: 24px;',
|
||||
' }',
|
||||
|
||||
' .cookie-input-container {',
|
||||
' margin-bottom: 15px;',
|
||||
' }',
|
||||
' .cookie-input-container {',
|
||||
' margin-bottom: 15px;',
|
||||
' }',
|
||||
|
||||
' #cookieInput {',
|
||||
' width: 100%;',
|
||||
' padding: 12px;',
|
||||
' border: 2px solid #e9ecef;',
|
||||
' border-radius: 8px;',
|
||||
' font-size: 0.95rem;',
|
||||
' resize: vertical;',
|
||||
' min-height: 120px;',
|
||||
' font-family: monospace;',
|
||||
' line-height: 1.4;',
|
||||
' }',
|
||||
' #cookieInput {',
|
||||
' width: 100%;',
|
||||
' padding: 12px;',
|
||||
' border: 2px solid #e9ecef;',
|
||||
' border-radius: 8px;',
|
||||
' font-size: 0.95rem;',
|
||||
' resize: vertical;',
|
||||
' min-height: 120px;',
|
||||
' font-family: monospace;',
|
||||
' line-height: 1.4;',
|
||||
' }',
|
||||
|
||||
' #cookieInput:focus {',
|
||||
' outline: none;',
|
||||
' border-color: #3498db;',
|
||||
' }',
|
||||
' #cookieInput:focus {',
|
||||
' outline: none;',
|
||||
' border-color: #3498db;',
|
||||
' }',
|
||||
|
||||
' .cookie-info {',
|
||||
' background: #f8f9fa;',
|
||||
' padding: 12px;',
|
||||
' border-radius: 8px;',
|
||||
' margin-bottom: 15px;',
|
||||
' }',
|
||||
' .cookie-info {',
|
||||
' background: #f8f9fa;',
|
||||
' padding: 12px;',
|
||||
' border-radius: 8px;',
|
||||
' margin-bottom: 15px;',
|
||||
' }',
|
||||
|
||||
' .cookie-info p {',
|
||||
' margin: 0;',
|
||||
' color: #2c3e50;',
|
||||
' font-size: 0.9rem;',
|
||||
' }',
|
||||
' .cookie-info p {',
|
||||
' margin: 0;',
|
||||
' color: #2c3e50;',
|
||||
' font-size: 0.9rem;',
|
||||
' margin-bottom: 8px;', // 添加底部间距
|
||||
' }',
|
||||
|
||||
' #currentTokenDisplay {',
|
||||
' color: #3498db;',
|
||||
' font-family: monospace;',
|
||||
' word-break: break-all;',
|
||||
' }',
|
||||
' .cookie-info p:last-child {',
|
||||
' margin-bottom: 0;', // 最后一个p元素不需要底部间距
|
||||
' }',
|
||||
|
||||
' .token-expiry {',
|
||||
' color: #7f8c8d;',
|
||||
' font-size: 0.85rem;',
|
||||
' }',
|
||||
|
||||
' .token-expiry.expired {',
|
||||
' color: #e74c3c;',
|
||||
' }',
|
||||
|
||||
' #currentTokenDisplay {',
|
||||
' color: #3498db;',
|
||||
' font-family: monospace;',
|
||||
' word-break: break-all;',
|
||||
' }',
|
||||
|
||||
// 添加获取cookie按钮样式
|
||||
' .get-cookie-link {',
|
||||
' position: fixed;',
|
||||
' left: 150px;', // 放在github图标左边
|
||||
' top: 22px;',
|
||||
' background: #2ecc71;', // 使用不同的颜色区分
|
||||
' color: white;',
|
||||
' border: none;',
|
||||
' padding: 8px 15px;',
|
||||
' border-radius: 5px;',
|
||||
' cursor: pointer;',
|
||||
' z-index: 1001;',
|
||||
' text-decoration: none;',
|
||||
' font-size: 14px;',
|
||||
' transition: background 0.3s ease;',
|
||||
' }',
|
||||
|
||||
' .get-cookie-link:hover {',
|
||||
' background: #27ae60;',
|
||||
' }',
|
||||
|
||||
' .advanced-mode-toggle {',
|
||||
' display: flex;',
|
||||
' align-items: center;',
|
||||
' margin-bottom: 15px;',
|
||||
' padding: 10px;',
|
||||
' background: #f8f9fa;',
|
||||
' border-radius: 8px;',
|
||||
' }',
|
||||
|
||||
' .advanced-mode-toggle input[type="checkbox"] {',
|
||||
' margin-right: 10px;',
|
||||
' }',
|
||||
|
||||
' .prompt-container {',
|
||||
' display: none;', // 默认隐藏
|
||||
' margin-bottom: 15px;',
|
||||
' }',
|
||||
|
||||
' .prompt-container.show {',
|
||||
' display: block;',
|
||||
' }',
|
||||
|
||||
' #promptInput {',
|
||||
' width: 100%;',
|
||||
' padding: 12px;',
|
||||
' border: 2px solid #e9ecef;',
|
||||
' border-radius: 8px;',
|
||||
' font-size: 0.95rem;',
|
||||
' resize: vertical;',
|
||||
' min-height: 120px;',
|
||||
' font-family: monospace;',
|
||||
' line-height: 1.4;',
|
||||
' }',
|
||||
|
||||
' #promptInput:focus {',
|
||||
' outline: none;',
|
||||
' border-color: #3498db;',
|
||||
' }',
|
||||
'</style>',
|
||||
'</head>',
|
||||
'<body>',
|
||||
'<a href="https://chat.qwenlm.ai/" target="_blank" rel="noopener noreferrer" class="get-cookie-link">',
|
||||
' 获取Cookie',
|
||||
'</a>',
|
||||
'<a href="https://github.com/Cunninger/ocr-based-qwen" target="_blank" rel="noopener noreferrer" class="github-link" title="View on GitHub">',
|
||||
' <svg class="github-icon" viewBox="0 0 16 16" fill="currentColor">',
|
||||
' <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>',
|
||||
@ -1166,11 +1297,20 @@ function getHTML() {
|
||||
'</div>',
|
||||
'<div class="cookie-info">',
|
||||
'<p>当前Token: <span id="currentTokenDisplay">未设置</span></p>',
|
||||
'<p>过期时间: <span id="tokenExpiryDisplay" class="token-expiry">未知</span></p>',
|
||||
'</div>',
|
||||
'<button class="save-btn" id="saveTokens">保存设置</button>',
|
||||
'</div>',
|
||||
// 添加 tokenList 容器
|
||||
'<div class="token-list" id="tokenList"></div>', // 添加这一行
|
||||
'<div class="advanced-mode-toggle">',
|
||||
' <input type="checkbox" id="advancedMode">',
|
||||
' <label for="advancedMode">高级模式 (自定义Prompt)</label>',
|
||||
'</div>',
|
||||
'<div class="prompt-container" id="promptContainer">',
|
||||
' <label for="promptInput">自定义Prompt</label>',
|
||||
' <textarea id="promptInput" placeholder="输入自定义prompt...">请识别图片中的内容,注意以下要求:\n对于数学公式和普通文本:\n1. 所有数学公式和数学符号都必须使用标准的LaTeX格式\n2. 行内公式使用单个$符号包裹,如:$x^2$\n3. 独立公式块使用两个$$符号包裹,如:$$\\sum_{i=1}^n i^2$$\n4. 普通文本保持原样,不要使用LaTeX格式\n5. 保持原文的段落格式和换行\n6. 明显的换行使用\\n表示\n7. 确保所有数学符号都被正确包裹在$或$$中\n\n对于验证码图片:\n1. 只输出验证码字符,不要加任何额外解释\n2. 忽略干扰线和噪点\n3. 注意区分相似字符,如0和O、1和l、2和Z等\n4. 验证码通常为4-6位字母数字组合\n\n不要输出任何额外的解释或说明</textarea>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
|
||||
'<div class="container">',
|
||||
@ -1370,7 +1510,7 @@ function getHTML() {
|
||||
' historyManager.displayHistory(currentToken);',
|
||||
' });',
|
||||
' tokenList.appendChild(div);',
|
||||
' });',
|
||||
' });',
|
||||
' const tokenInput = document.getElementById(\'tokenInput\');',
|
||||
' if (tokenInput) {',
|
||||
' tokenInput.value = tokens.join(",");',
|
||||
@ -1404,6 +1544,9 @@ function getHTML() {
|
||||
' document.getElementById(\'currentTokenDisplay\').textContent = ',
|
||||
' token.slice(0, 10) + "..." + token.slice(-10);',
|
||||
' ',
|
||||
' // 更新过期时间显示',
|
||||
' updateTokenExpiryDisplay(token);',
|
||||
' ',
|
||||
' alert(\'设置已保存\');',
|
||||
' });',
|
||||
|
||||
@ -1421,6 +1564,12 @@ function getHTML() {
|
||||
' }',
|
||||
|
||||
' const savedCookie = localStorage.getItem(\'imageRecognitionCookie\');',
|
||||
' if (!savedCookie) {',
|
||||
' alert(\'请先设置Cookie\');',
|
||||
' sidebar.classList.add(\'open\');',
|
||||
' return;',
|
||||
' }',
|
||||
|
||||
' // 显示图片预览',
|
||||
' const reader = new FileReader();',
|
||||
' let imageData;',
|
||||
@ -1443,8 +1592,7 @@ function getHTML() {
|
||||
' const uploadResponse = await fetch(\'/proxy/upload\', {',
|
||||
' method: \'POST\',',
|
||||
' headers: {',
|
||||
' \'Authorization\': \'Bearer \' + currentToken,',
|
||||
' \'x-custom-cookie\': savedCookie || \'\', // 添加 cookie 到请求头',
|
||||
' \'x-custom-cookie\': savedCookie,',
|
||||
' },',
|
||||
' body: formData,',
|
||||
' });',
|
||||
@ -1455,22 +1603,21 @@ function getHTML() {
|
||||
' // 识别图片',
|
||||
' const recognizeResponse = await fetch(\'/recognize\', {',
|
||||
' method: \'POST\',',
|
||||
' headers: { \'Content-Type\': \'application/json\' },',
|
||||
' body: JSON.stringify({ ',
|
||||
' token: currentToken, ',
|
||||
' imageId: uploadData.id ',
|
||||
' }),',
|
||||
' headers: { ',
|
||||
' \'Content-Type\': \'application/json\',',
|
||||
' \'x-custom-cookie\': savedCookie,',
|
||||
' \'x-advanced-mode\': advancedMode.checked, // 添加高级模式状态',
|
||||
' \'x-custom-prompt\': btoa(encodeURIComponent(promptInput.value)), // Base64编码',
|
||||
' },',
|
||||
' body: JSON.stringify({ imageId: uploadData.id }),',
|
||||
' });',
|
||||
|
||||
' const recognizeData = await recognizeResponse.json();',
|
||||
|
||||
' // 修改这里:使用新的响应格式',
|
||||
' if (!recognizeData.success) {',
|
||||
' throw new Error(recognizeData.error || \'识别失败\');',
|
||||
' }',
|
||||
|
||||
' const result = recognizeData.result || \'识别失败\';',
|
||||
' // 保存原始文本到属性中,确保 LaTeX 格式完整',
|
||||
' resultDiv.setAttribute(\'data-original-text\', result);',
|
||||
' resultDiv.innerHTML = result;',
|
||||
' waitForMathJax(() => {',
|
||||
@ -1702,12 +1849,12 @@ function getHTML() {
|
||||
' await navigator.clipboard.writeText(result);',
|
||||
' btn.textContent = \'已复制\';',
|
||||
' btn.classList.add(\'copied\');',
|
||||
|
||||
|
||||
' setTimeout(() => {',
|
||||
' btn.textContent = \'复制结果\';',
|
||||
' btn.classList.remove(\'copied\');',
|
||||
' }, 2000);',
|
||||
|
||||
|
||||
' return true;',
|
||||
' } catch (err) {',
|
||||
' console.error(\'复制失败:\', err);',
|
||||
@ -1859,12 +2006,90 @@ function getHTML() {
|
||||
' currentToken = token;',
|
||||
' document.getElementById(\'currentTokenDisplay\').textContent = ',
|
||||
' token.slice(0, 10) + "..." + token.slice(-10);',
|
||||
' updateTokenExpiryDisplay(token);', // 修改这行,确保正确的字符串格式
|
||||
' }',
|
||||
' }',
|
||||
' }',
|
||||
|
||||
' // 初始化时调用loadSettings',
|
||||
' loadSettings();',
|
||||
|
||||
' // 高级模式切换处理',
|
||||
' const advancedMode = document.getElementById(\'advancedMode\');',
|
||||
' const promptContainer = document.getElementById(\'promptContainer\');',
|
||||
' const promptInput = document.getElementById(\'promptInput\');',
|
||||
|
||||
' advancedMode.addEventListener(\'change\', () => {',
|
||||
' promptContainer.classList.toggle(\'show\', advancedMode.checked);',
|
||||
' localStorage.setItem(\'advancedMode\', advancedMode.checked);',
|
||||
' localStorage.setItem(\'customPrompt\', promptInput.value);',
|
||||
' });',
|
||||
|
||||
' // 加载保存的高级模式设置',
|
||||
' function loadAdvancedSettings() {',
|
||||
' const savedMode = localStorage.getItem(\'advancedMode\');',
|
||||
' const savedPrompt = localStorage.getItem(\'customPrompt\');',
|
||||
' if (savedMode === \'true\') {',
|
||||
' advancedMode.checked = true;',
|
||||
' promptContainer.classList.add(\'show\');',
|
||||
' }',
|
||||
' if (savedPrompt) {',
|
||||
' promptInput.value = savedPrompt;',
|
||||
' }',
|
||||
' }',
|
||||
|
||||
' loadAdvancedSettings();',
|
||||
|
||||
' // JWT解析函数',
|
||||
' function parseJwt(token) {',
|
||||
' try {',
|
||||
' const base64Url = token.split(\'.\')[1];',
|
||||
' const base64 = base64Url.replace(/-/g, \'+\').replace(/_/g, \'/\');',
|
||||
' const jsonPayload = decodeURIComponent(atob(base64).split(\'\').map(function(c) {',
|
||||
' return \'%\' + (\'00\' + c.charCodeAt(0).toString(16)).slice(-2);',
|
||||
' }).join(\'\'));',
|
||||
' return JSON.parse(jsonPayload);',
|
||||
' } catch (e) {',
|
||||
' console.error(\'JWT解析错误:\', e);',
|
||||
' return null;',
|
||||
' }',
|
||||
' }',
|
||||
|
||||
' // 更新Token过期时间显示',
|
||||
' function updateTokenExpiryDisplay(token) {',
|
||||
' const expiryDisplay = document.getElementById(\'tokenExpiryDisplay\');',
|
||||
' if (!token) {',
|
||||
' expiryDisplay.textContent = \'未设置\';',
|
||||
' expiryDisplay.classList.remove(\'expired\');',
|
||||
' return;',
|
||||
' }',
|
||||
' ',
|
||||
' const decoded = parseJwt(token);',
|
||||
' if (!decoded || !decoded.exp) {',
|
||||
' expiryDisplay.textContent = \'无法解析过期时间\';',
|
||||
' return;',
|
||||
' }',
|
||||
' ',
|
||||
' const expiryDate = new Date(decoded.exp * 1000);',
|
||||
' const now = new Date();',
|
||||
' const isExpired = expiryDate < now;',
|
||||
' ',
|
||||
' expiryDisplay.textContent = expiryDate.toLocaleString(\'zh-CN\', {',
|
||||
' year: \'numeric\',',
|
||||
' month: \'2-digit\',',
|
||||
' day: \'2-digit\',',
|
||||
' hour: \'2-digit\',',
|
||||
' minute: \'2-digit\',',
|
||||
' second: \'2-digit\'',
|
||||
' });',
|
||||
' ',
|
||||
' if (isExpired) {',
|
||||
' expiryDisplay.classList.add(\'expired\');',
|
||||
' expiryDisplay.textContent += \' (已过期)\';',
|
||||
' } else {',
|
||||
' expiryDisplay.classList.remove(\'expired\');',
|
||||
' }',
|
||||
' }',
|
||||
'</script>',
|
||||
'</body>',
|
||||
'</html>'
|
||||
|
Loading…
x
Reference in New Issue
Block a user