Compare commits

...

3 Commits

Author SHA1 Message Date
MobKBK
f6e09689d6 添加软件架构图到 README
Some checks failed
Android CI / build (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-04 17:37:56 +08:00
MobKBK
9002563e75 添加完整 PC Server 部署指南
Some checks failed
Android CI / build (push) Has been cancelled
- pc-server/README.md 重写:从零开始的全流程部署说明
- 包含 conda 环境创建、MinerU/OCRmyPDF/Tesseract/Ghostscript 安装步骤
- main.py 配置项说明:哪些需要改、哪些自动检测
- 模型下载指令、国内网络镜像方案
- 常见故障排查表
- 根 README 简化部署章节,指向详细文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-04 17:22:59 +08:00
MobKBK
2cc6fd34eb 更名为 FAIRSCAN_SURPORT_BY_CYY (FSBC)
Some checks failed
Android CI / build (push) Has been cancelled
- app_name → FSBC
- PC 服务器标题/页面名称 → FSBC
- 日志标签:FairScan → FSBC
- PDF 创建者/邮件标题/诊断报告 → FSBC
- README 重写:新增图传/上传/MinerU/OCRmyPDF/任务面板完整说明

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-04 17:13:34 +08:00
11 changed files with 359 additions and 143 deletions

242
README.md
View File

@@ -1,147 +1,155 @@
<p align="center"> <p align="center">
<img src="metadata/en-US/images/icon.png" alt="FairScan icon" width="120" /> <img src="app/src/main/res/drawable/icon.png" alt="FSBC icon" width="120" />
</p> </p>
<h1 align="center">FairScan</h1> <h1 align="center">FAIRSCAN_SURPORT_BY_CYY</h1>
<h3 align="center"><b>FSBC</b> — 扫描 + 图传 + 文档处理一体化</h3>
<p align="center"> <p align="center">
An Android app to scan your documents — 基于 <a href="https://github.com/pynicolas/FairScan">FairScan</a> 增强,<br/>
<br/><b>simple</b> and <b>respectful</b>. 新增局域网实时图传、PDF 上传、MinerU / OCRmyPDF 云端处理、手机端任务管理面板。
</p> </p>
<p align="center"> <p align="center">
<a href="https://github.com/pynicolas/FairScan/releases"><img src="https://img.shields.io/github/v/release/pynicolas/FairScan?logo=github" /></a> <img src="docs/main_tree.png" alt="FSBC 架构图" width="800" />
<a href="LICENSE"><img alt="License" src="https://img.shields.io/github/license/pynicolas/FairScan?color=blue"></a>
</p> </p>
<h3 align="center">
<b>Get it on:</b>
<a href="https://f-droid.org/en/packages/org.fairscan.app/">F-Droid</a> ·
<a href="https://play.google.com/store/apps/details?id=org.fairscan.app">Google Play</a> ·
<a href="https://github.com/pynicolas/FairScan/releases">GitHub</a>
</h3>
--- ---
FairScan is an Android app to **scan documents quickly, easily and privately**. ## 新增功能CYY 增强版)
It's designed to be **simple**: users get a clean, shareable PDF in seconds, with no manual adjustments.<br> ### 实时网络图传
And **respectful**: open source, minimal permissions, no tracking, no ads. - 手机通过 WebSocket 将 JPEG 帧发送到 PC
- PC 浏览器实时显示画面(`http://<host>:2026`
- 支持帧率控制(无限制 / 15fps / 10fps / 5fps
- 三档压缩质量可选(低 / 均衡 / 高)
- Website: https://fairscan.org ### PDF 上传 + 云端处理
- Blog: https://fairscan.org/blog/ - 导出页一键上传 PDF 到 PC 服务器
- 三种模式:仅传输 / 上传并处理 OCR PDF / 上传并处理 Markdown
- 上传进度实时显示
### MinerU → Markdown 数字化
- 真实接入 MinerU pipeline 后端v3.0.9
- 将扫描 PDF 转为结构化 Markdown + 图片
- 自动打包 `{name}_result.zip`.md + images/
- GPU 加速RTX 4060 8GB约 3~8 秒/页
### OCRmyPDF → 可搜索双层 PDF
- 接入 OCRmyPDF v15.4.4,生成真正可搜索的双层 PDF
- Tesseract 5.5.2 OCR 引擎,支持中文/英语/日语/韩语
- Ctrl+F 搜索、屏幕阅读器无障碍
### 手机端任务管理面板
- 导出页底部显示所有上传处理任务
- 四种状态:排队中 / 处理中(进度条) / 已完成 / 失败
- 2 秒轮询自动更新状态,完成后可下载到指定目录
- SAF 目录选择器,下载进度实时显示
### PC 管理面板
- 浏览器访问 `http://<host>:2026/dashboard`
- 文件列表 + 任务列表 + 统计卡片
- 支持下载原始文件和处理产物
- 自动刷新
--- ---
## Contributing ## 原版功能FairScan
Contributions are welcome, but please read the guidelines first: [CONTRIBUTING.md](CONTRIBUTING.md) - **清晰无干扰的界面**,一键扫描
- **自动文档检测**:基于自定义分割模型 (LiteRT)
- **自动透视校正** + **图像增强** (OpenCV)
- **快速 PDF 生成**,无需手动调整
- **完全离线** — 原版无网络权限
- **最小权限**,无跟踪,无广告
- **开源** GPLv3
--- ---
## Screenshots ## PC 服务器部署
| Scan | Preview | Save & Share | **详细部署指南**[pc-server/README.md](pc-server/README.md)
|------|---------|--------------|
| ![](metadata/en-US/images/phoneScreenshots/1.jpg) | ![](metadata/en-US/images/phoneScreenshots/2.jpg) | ![](metadata/en-US/images/phoneScreenshots/3.jpg) |
--- ### 快速启动(环境已配好)
## Features
- **Clear, distraction-free interface**
- **Easy flow**: scan, review if needed, save or share
- **Automatic document detection** using a custom segmentation model
- **Automatic perspective correction**
- **Automatic image enhancement**
- **Fast PDF generation** with no manual adjustments
- **Fully offline** the app has *no* internet permission
- **Minimal permissions**
- **Open source**, GPLv3
---
## What FairScan is not
FairScan is **not** intended to:
- provide fine-grained manual control over document processing
- replicate all features found in other scanning apps
- optimize for highly specific use cases at the expense of simplicity
---
## Compatibility
FairScan works on any device that:
- runs **Android 8.0+**
- has a camera
---
## Experimental: Scan to PDF via intent
FairScan can be invoked by other Android applications to perform a document scan and return a generated PDF.
This feature is **experimental** and intended for developers who want to rely on FairScan as a
simple, privacy-respecting scanning tool.
The intent contract and behavior may change between versions, and backward compatibility
is not guaranteed at this stage.
Intent action: `org.fairscan.app.action.SCAN_TO_PDF`
This is an **implicit intent** that launches FairScan in a dedicated external mode.
When started via this intent:
- FairScan opens directly in scan mode
- the user scans one or more pages
- FairScan generates a single PDF
- the resulting PDF is returned to the calling application as a URI with a limited lifetime
- the calling application should immediately copy the content of the URI as FairScan deletes it later
See an example app: [fairscan-intent-sample](https://github.com/pynicolas/fairscan-intent-sample)
---
## Technical details
FairScan uses:
- [Jetpack Compose](https://developer.android.com/compose) for the UI
- [CameraX](https://developer.android.com/media/camera/camerax) for image capture
- [LiteRT](https://ai.google.dev/edge/litert) to run the custom segmentation model for automatic document detection
- [OpenCV](https://opencv.org/) for perspective correction and image enhancement
- [PDFBox-Android](https://github.com/TomRoush/PdfBox-Android) for PDF generation
---
## The segmentation model
FairScan uses a custom-trained image segmentation model to detect documents:<br>
https://github.com/pynicolas/fairscan-segmentation-model
It's based on a fully public dataset that is available here:<br>
https://github.com/pynicolas/fairscan-dataset
The build system automatically downloads the model using
[`download-tflite.gradle.kts`](app/download-tflite.gradle.kts).
Related blog posts:
- [*Making document detection more reliable*](https://fairscan.org/blog/automatic-document-detection/)
- [*Building a public dataset for FairScan*](https://fairscan.org/blog/building_a_public_dataset/)
---
## Build
To build an APK:
```bash ```bash
./gradlew clean check assembleRelease conda activate MinerU
cd pc-server
python main.py
``` ```
To build an Android App Bundle:
服务启动在 `http://0.0.0.0:2026`
### 环境一览
| 组件 | 版本 | 用途 |
|------|------|------|
| Python | 3.10 | conda 环境 `MinerU` |
| PyTorch | 2.6 + CUDA 12.4 | GPU 推理 |
| MinerU | git clone + pip install -e | markdown 处理 |
| OCRmyPDF | v15.4.4 | 可搜索双层 PDF |
| Tesseract | 5.5.2 + chi_sim/eng | OCR 引擎 |
| Ghostscript | 10.07.1 | PDF 后处理 |
### API 端点
| 端点 | 方法 | 功能 |
|------|------|------|
| `/health` | GET | 健康检查 |
| `/stream` | WS | 接收图传帧 |
| `/` | GET | 图传预览页面 |
| `/dashboard` | GET | 管理面板 |
| `/api/dashboard` | GET | 管理面板 JSON |
| `/upload/pdf` | POST | 上传 PDF |
| `/tasks/process` | POST | 创建处理任务 |
| `/tasks/{taskId}` | GET | 查询任务状态 |
| `/tasks/{taskId}/artifacts` | GET | 查询产物列表 |
| `/artifacts/{id}/download` | GET | 下载产物 |
| `/files/{fileId}/download` | GET | 下载原始文件 |
---
## 手机端配置
1. 进入设置 → 网络协作
2. 填写 PC 主机 IP 和端口(默认 2026
3. 点击"测试连接"验证
4. 扫描文档后在导出页选择上传/处理
---
## 技术栈
| 层 | 技术 |
|----|------|
| Android UI | Jetpack Compose |
| 相机 | CameraX |
| 文档检测 | LiteRT + 自定义分割模型 |
| 图像处理 | OpenCV |
| PDF 生成 | PDFBox-Android |
| 网络 | OkHttp 4.12 + WebSocket |
| PC 服务 | Python FastAPI + Uvicorn |
| Markdown 处理 | MinerU (pipeline backend) |
| OCR PDF | OCRmyPDF + Tesseract + Ghostscript |
---
## 构建
```bash ```bash
# Android APK
./gradlew clean check assembleRelease
# Android App Bundle
./gradlew clean check :app:bundleRelease ./gradlew clean check :app:bundleRelease
``` ```
## License ## 许可
This project is licensed under the GNU GPLv3. See [LICENSE](LICENSE) for details.
本项目基于 FairScan (GPLv3) 修改。详见 [LICENSE](LICENSE)。
## 致谢
- 原项目 [FairScan](https://github.com/pynicolas/FairScan)
- [MinerU](https://github.com/opendatalab/MinerU)
- [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF)

View File

@@ -328,7 +328,7 @@ class MainActivity : ComponentActivity() {
when (event) { when (event) {
is AboutEvent.CopyLogs -> { is AboutEvent.CopyLogs -> {
clipboard.setClipEntry( clipboard.setClipEntry(
ClipData.newPlainText("FairScan logs", event.logs).toClipEntry() ClipData.newPlainText("FSBC logs", event.logs).toClipEntry()
) )
showToast(msgCopiedLogs) showToast(msgCopiedLogs)
} }

View File

@@ -32,7 +32,7 @@ class AndroidPdfWriter : PdfWriter {
override suspend fun writePdfFromJpegs(pages: List<PageToExport>, outputStream: OutputStream): Int { override suspend fun writePdfFromJpegs(pages: List<PageToExport>, outputStream: OutputStream): Int {
val doc = PDDocument() val doc = PDDocument()
doc.documentInformation.creationDate = Calendar.getInstance() doc.documentInformation.creationDate = Calendar.getInstance()
doc.documentInformation.creator = "FairScan ${BuildConfig.VERSION_NAME}" doc.documentInformation.creator = "FSBC ${BuildConfig.VERSION_NAME}"
doc.use { document -> doc.use { document ->
for (page in pages) { for (page in pages) {
val image = JPEGFactory.createFromByteArray(document, page.jpeg.get().bytes) val image = JPEGFactory.createFromByteArray(document, page.jpeg.get().bytes)

View File

@@ -52,7 +52,7 @@ class AboutViewModel(container: AppContainer, val imageRepository: ImageReposito
private fun buildFullLogs(): String { private fun buildFullLogs(): String {
val header = buildString { val header = buildString {
appendLine("FairScan diagnostics report") appendLine("FSBC diagnostics report")
appendLine("App version: ${BuildConfig.VERSION_NAME}") appendLine("App version: ${BuildConfig.VERSION_NAME}")
appendLine("Android version: ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})") appendLine("Android version: ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})")
appendLine("Generated: ${LocalDateTime.now()}") appendLine("Generated: ${LocalDateTime.now()}")

View File

@@ -34,7 +34,7 @@ fun createEmailWithImageIntent(context: Context, imageFile: File?): Intent {
putExtra(Intent.EXTRA_EMAIL, arrayOf(EMAIL_ADDRESS)) putExtra(Intent.EXTRA_EMAIL, arrayOf(EMAIL_ADDRESS))
putExtra( putExtra(
Intent.EXTRA_SUBJECT, Intent.EXTRA_SUBJECT,
"FairScan ${BuildConfig.VERSION_NAME}" "FSBC ${BuildConfig.VERSION_NAME}"
) )
if (imageFile != null) { if (imageFile != null) {
val uri = uriForFile(context, imageFile) val uri = uriForFile(context, imageFile)

View File

@@ -232,7 +232,7 @@ fun CameraScreen(
isTorchEnabled), isTorchEnabled),
onCapture = { onCapture = {
previewView?.bitmap?.let { previewView?.bitmap?.let {
Log.i("FairScan", "Pressed <Capture>") Log.i("FSBC", "Pressed <Capture>")
cameraViewModel.onCapturePressed(it) cameraViewModel.onCapturePressed(it)
captureController.takePicture( captureController.takePicture(
onImageCaptured = { imageProxy, opticalMeasures -> onImageCaptured = { imageProxy, opticalMeasures ->

View File

@@ -184,7 +184,7 @@ class ExportViewModel(container: AppContainer, val imageRepository: ImageReposit
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
val message = "Failed to prepare $exportFormat export" val message = "Failed to prepare $exportFormat export"
logger.e("FairScan", message, e) logger.e("FSBC", message, e)
_uiState.update { _uiState.update {
it.copy(error = ExportError.OnPrepareOrShare(message, e)) it.copy(error = ExportError.OnPrepareOrShare(message, e))
} }
@@ -256,7 +256,7 @@ class ExportViewModel(container: AppContainer, val imageRepository: ImageReposit
_uiState.update { it.copy(hasShared = true) } _uiState.update { it.copy(hasShared = true) }
} catch (e: Exception) { } catch (e: Exception) {
val message = "Failed to prepare share" val message = "Failed to prepare share"
logger.e("FairScan", message, e) logger.e("FSBC", message, e)
_uiState.update { it.copy(error = ExportError.OnPrepareOrShare(message, e)) } _uiState.update { it.copy(error = ExportError.OnPrepareOrShare(message, e)) }
} }
} }
@@ -280,13 +280,13 @@ class ExportViewModel(container: AppContainer, val imageRepository: ImageReposit
save(context, saveDir, exportFormat) save(context, saveDir, exportFormat)
} }
} catch (e: MissingExportDirPermissionException) { } catch (e: MissingExportDirPermissionException) {
logger.e("FairScan", "Missing export dir permission", e) logger.e("FSBC", "Missing export dir permission", e)
_uiState.update { _uiState.update {
it.copy(error = it.copy(error =
ExportError.OnSave(R.string.error_export_dir_permission_lost, saveDir)) ExportError.OnSave(R.string.error_export_dir_permission_lost, saveDir))
} }
} catch (e: Exception) { } catch (e: Exception) {
logger.e("FairScan", "Failed to save PDF", e) logger.e("FSBC", "Failed to save PDF", e)
_uiState.update { _uiState.update {
it.copy(error = ExportError.OnSave(R.string.error_save, saveDir, e)) it.copy(error = ExportError.OnSave(R.string.error_save, saveDir, e))
} }

View File

@@ -1,7 +1,7 @@
<resources> <resources>
<string name="about">About</string> <string name="about">About</string>
<string name="add_page">Add page</string> <string name="add_page">Add page</string>
<string name="app_name" translatable="false">FairScan</string> <string name="app_name" translatable="false">FSBC</string>
<string name="app_tagline">A simple and respectful application to scan your documents.</string> <string name="app_tagline">A simple and respectful application to scan your documents.</string>
<string name="apply">Apply</string> <string name="apply">Apply</string>
<string name="back">Back</string> <string name="back">Back</string>

BIN
docs/main_tree.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -1,15 +1,223 @@
# PC Server for FairScan real-time camera streaming # FSBC PC Server — 部署指南
#
# This is a minimal test server that:
# - Receives WebSocket frames from the Android app
# - Displays them in a browser
# - Provides GET /health for connection testing
## Quick Start 本文档写给**在全新机器上部署此项目的人**,从零开始搭建 PC 端处理服务。
---
## 1. 硬件要求
| 项目 | 最低 | 推荐 |
|------|------|------|
| GPU | 无(纯 CPU | NVIDIA 8GB VRAM+ |
| 内存 | 8 GB | 16 GB |
| 磁盘 | 20 GB | 50 GB模型缓存 ~5GB |
| 系统 | Windows / Linux / macOS | — |
> 无 GPU 也可运行,但 MinerU 处理速度会显著下降10~30 秒/页)。
---
## 2. 环境搭建(完整步骤)
### 2.1 安装 Miniconda
从 https://docs.conda.io/en/latest/miniconda.html 下载安装。安装后打开 `Anaconda Prompt`Windows或终端。
### 2.2 创建 conda 环境
```bash ```bash
conda create -n MinerU python=3.10 -y
conda activate MinerU
```
### 2.3 安装 PyTorchCUDA 版)
```bash
# CUDA 12.4
pip install torch==2.6.0 torchvision==0.21.0 --index-url https://download.pytorch.org/whl/cu124
# 如果无 NVIDIA GPU装 CPU 版:
# pip install torch==2.6.0 --index-url https://download.pytorch.org/whl/cpu
```
### 2.4 安装 MinerU
```bash
git clone https://github.com/opendatalab/MinerU.git
cd MinerU
# 注意:最新版需要 Python >= 3.11,但我们的环境是 3.10
# 使用兼容版本(例如 v3.0.9 tag 或对应 commit
pip install -e .
cd ..
```
> 如果 `pip install -e .` 因 Python 版本不兼容失败,找一个兼容 3.10 的 tag
> ```bash
> git tag | tail -20 # 查看可用版本
> git checkout <tag> # 切换到一个兼容的版本
> ```
### 2.5 安装 OCRmyPDF
```bash
git clone https://github.com/ocrmypdf/OCRmyPDF.git ocRmypdf
cd ocRmypdf
# 最新版需要 Python >= 3.11,用 v15.4.4(支持 Python 3.10
git checkout v15.4.4
pip install -e .
cd ..
```
### 2.6 安装 Tesseract + Ghostscript
```bash
conda install -c conda-forge tesseract ghostscript -y
```
### 2.7 下载 Tesseract 语言包
```bash
# 中文简体
curl -L -o "$CONDA_PREFIX/Library/share/tessdata/chi_sim.traineddata" \
"https://github.com/tesseract-ocr/tessdata/raw/main/chi_sim.traineddata"
# 英语
curl -L -o "$CONDA_PREFIX/Library/share/tessdata/eng.traineddata" \
"https://github.com/tesseract-ocr/tessdata/raw/main/eng.traineddata"
# 可选:日语、韩语
# curl -L -o "$CONDA_PREFIX/Library/share/tessdata/jpn.traineddata" \
# "https://github.com/tesseract-ocr/tessdata/raw/main/jpn.traineddata"
# curl -L -o "$CONDA_PREFIX/Library/share/tessdata/kor.traineddata" \
# "https://github.com/tesseract-ocr/tessdata/raw/main/kor.traineddata"
```
验证:
```bash
tesseract --list-langs
# 应输出chi_sim, eng
```
### 2.8 安装 FSBC PC Server 依赖
```bash
cd pc-server
pip install -r requirements.txt pip install -r requirements.txt
```
### 2.9 下载 MinerU 模型
```bash
python -c "
from huggingface_hub import snapshot_download
# Pipeline 模型(~2.5 GB
snapshot_download('opendatalab/PDF-Extract-Kit-1.0')
# VLM 模型(~2 GB可选仅 VLM 后端需要)
# snapshot_download('opendatalab/MinerU2.5-2509-1.2B')
"
```
> 国内网络无法访问 huggingface.co设置镜像
> ```bash
> export HF_ENDPOINT=https://hf-mirror.com
> ```
> 模型下载完成后,`main.py` 会自动设置 `HF_HUB_OFFLINE=1` 使用本地缓存。
---
## 3. 配置 main.py
### 3.1 不需要改的部分
以下配置由 `main.py` **自动检测**,通常不需要手动修改:
```python
# HF_HUB_OFFLINE=1 — 自动设置,强制使用本地模型缓存
# TESSDATA_PREFIX — 自动从 CONDA_PREFIX 推导路径
```
### 3.2 可能需要改的部分
| 配置项 | 位置 | 默认值 | 说明 |
|--------|------|--------|------|
| 服务端口 | `main.py` 底部 `port = 2026` | 2026 | 如果端口被占用,改这里 |
| 上传目录 | `UPLOAD_DIR` | `./uploads` | 上传的 PDF 存放位置 |
| 任务目录 | `TASKS_DIR` | `./tasks` | 处理产物存放位置 |
| MinerU 后端 | `backend` | `"pipeline"` | pipeline/vlm/hybrid |
| Markdown 解析方式 | `parse_method` | `"auto"` | auto/txt/ocr |
| OCR 语言 | `lang` 映射 | ch→chi_sim | 在 `process_with_mineru` 中修改映射 |
### 3.3 如果你不用 conda
如果你直接在系统 Python 中安装而非 conda 环境,需要手动设置:
```python
# 在 main.py 顶部添加:
os.environ["TESSDATA_PREFIX"] = "C:/path/to/your/tessdata"
os.environ["HF_HUB_OFFLINE"] = "1" # 如果模型已下载
```
---
## 4. 启动服务
```bash
conda activate MinerU
cd pc-server
python main.py python main.py
``` ```
Open http://localhost:2026 in a browser to see the stream. 输出:
```
🚀 FSBC Server starting on http://0.0.0.0:2026
Stream: http://localhost:2026
Dashboard: http://localhost:2026/dashboard
Health: http://localhost:2026/health
```
### 4.1 验证
```bash
# 健康检查
curl http://localhost:2026/health
# → {"status":"ok","name":"FSBC-PC","features":["stream","upload","tasks"]}
# 浏览器打开管理面板
# http://localhost:2026/dashboard
# 浏览器打开图传预览
# http://localhost:2026
```
---
## 5. 故障排查
| 问题 | 原因 | 解决 |
|------|------|------|
| `ModuleNotFoundError: mineru` | MinerU 未安装 | 执行步骤 2.4 |
| `ModuleNotFoundError: ocrmypdf` | OCRmyPDF 未安装 | 执行步骤 2.5 |
| `Could not find program 'tesseract'` | Tesseract 未安装 | 执行步骤 2.6 |
| `Could not find program 'gs'` | Ghostscript 未安装 | 执行步骤 2.6 |
| MinerU SSL 错误连不上 huggingface.co | 国内网络限制 | 设置 `HF_ENDPOINT` 镜像后重新下载模型 |
| `TESSDATA_PREFIX` 路径不对 | 未用 conda 或路径不同 | 手动设置 `os.environ["TESSDATA_PREFIX"]` |
| WebSocket 404 / uvicorn 警告 | 缺少 websockets 包 | `pip install websockets` |
| `'Pdf' object has no attribute 'check'` | pikepdf 版本过新 | `pip install "pikepdf>=8.7.1,<9"` |
| OCR 结果不理想 | 语言包不对 | 确认 tesseract --list-langs 包含目标语言 |
---
## 6. 目录结构
```
pc-server/
├── main.py # 主服务入口
├── requirements.txt # Python 依赖
├── README.md # 本文件
├── uploads/ # 上传的原始 PDF自动创建
└── tasks/ # 处理任务输出(自动创建)
└── {taskId}/
├── {name}.md # Markdown 产物
├── {name}_result.zip # .md + images 打包
└── {name}_ocr.pdf # OCR 双层 PDF
```

View File

@@ -1,5 +1,5 @@
""" """
FairScan PC Server — Streaming, PDF upload & real MinerU task processing. FAIRSCAN_SURPORT_BY_CYY (FSBC) PC Server
Endpoints: Endpoints:
Streaming: Streaming:
@@ -41,7 +41,7 @@ from mineru.cli.common import aio_do_parse, read_fn
import ocrmypdf import ocrmypdf
from loguru import logger from loguru import logger
app = FastAPI(title="FairScan PC Server") app = FastAPI(title="FSBC PC Server")
# ── Configuration ───────────────────────────────────────────────────────────── # ── Configuration ─────────────────────────────────────────────────────────────
@@ -66,7 +66,7 @@ STREAM_PAGE = """\
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>FairScan Stream</title> <title>FSBC Stream</title>
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
body { body {
@@ -119,7 +119,7 @@ STREAM_PAGE = """\
</head> </head>
<body> <body>
<div class="nav-bar"> <div class="nav-bar">
<h1>📷 FairScan Live Stream</h1> <h1>📷 FSBC Live Stream</h1>
<div class="nav-links"> <div class="nav-links">
<a href="/" class="active">📷 图传预览</a> <a href="/" class="active">📷 图传预览</a>
<a href="/dashboard">📊 管理面板</a> <a href="/dashboard">📊 管理面板</a>
@@ -177,7 +177,7 @@ async def health():
"""Health check endpoint used by Android for connection testing.""" """Health check endpoint used by Android for connection testing."""
return JSONResponse({ return JSONResponse({
"status": "ok", "status": "ok",
"name": "FairScan-PC", "name": "FSBC-PC",
"features": ["stream", "upload", "tasks"], "features": ["stream", "upload", "tasks"],
"streamStats": { "streamStats": {
"framesReceived": stream_stats["frames_received"], "framesReceived": stream_stats["frames_received"],
@@ -410,7 +410,7 @@ DASHBOARD_PAGE = """\
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>FairScan Dashboard</title> <title>FSBC Dashboard</title>
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
body { body {
@@ -482,7 +482,7 @@ DASHBOARD_PAGE = """\
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<h1>📊 FairScan Dashboard</h1> <h1>📊 FSBC Dashboard</h1>
<div class="nav-links"> <div class="nav-links">
<a href="/">📷 图传预览</a> <a href="/">📷 图传预览</a>
<a href="/dashboard" style="background:#7c8dff22;border-color:#7c8dff;">📊 管理面板</a> <a href="/dashboard" style="background:#7c8dff22;border-color:#7c8dff;">📊 管理面板</a>
@@ -791,7 +791,7 @@ async def process_with_mineru(task_id: str):
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
port = 2026 port = 2026
print(f"🚀 FairScan PC Server starting on http://0.0.0.0:{port}") print(f"🚀 FSBC Server starting on http://0.0.0.0:{port}")
print(f" Stream: http://localhost:{port}") print(f" Stream: http://localhost:{port}")
print(f" Dashboard: http://localhost:{port}/dashboard") print(f" Dashboard: http://localhost:{port}/dashboard")
print(f" Health: http://localhost:{port}/health") print(f" Health: http://localhost:{port}/health")