- app_name → FSBC - PC 服务器标题/页面名称 → FSBC - 日志标签:FairScan → FSBC - PDF 创建者/邮件标题/诊断报告 → FSBC - README 重写:新增图传/上传/MinerU/OCRmyPDF/任务面板完整说明 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
248
README.md
248
README.md
@@ -1,147 +1,149 @@
|
|||||||
<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">
|
---
|
||||||
<a href="https://github.com/pynicolas/FairScan/releases"><img src="https://img.shields.io/github/v/release/pynicolas/FairScan?logo=github" /></a>
|
|
||||||
<a href="LICENSE"><img alt="License" src="https://img.shields.io/github/license/pynicolas/FairScan?color=blue"></a>
|
## 新增功能(CYY 增强版)
|
||||||
</p>
|
|
||||||
<h3 align="center">
|
### 实时网络图传
|
||||||
<b>Get it on:</b>
|
- 手机通过 WebSocket 将 JPEG 帧发送到 PC
|
||||||
<a href="https://f-droid.org/en/packages/org.fairscan.app/">F-Droid</a> ·
|
- PC 浏览器实时显示画面(`http://<host>:2026`)
|
||||||
<a href="https://play.google.com/store/apps/details?id=org.fairscan.app">Google Play</a> ·
|
- 支持帧率控制(无限制 / 15fps / 10fps / 5fps)
|
||||||
<a href="https://github.com/pynicolas/FairScan/releases">GitHub</a>
|
- 三档压缩质量可选(低 / 均衡 / 高)
|
||||||
</h3>
|
|
||||||
|
### PDF 上传 + 云端处理
|
||||||
|
- 导出页一键上传 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`
|
||||||
|
- 文件列表 + 任务列表 + 统计卡片
|
||||||
|
- 支持下载原始文件和处理产物
|
||||||
|
- 自动刷新
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
FairScan is an Android app to **scan documents quickly, easily and privately**.
|
## 原版功能(FairScan)
|
||||||
|
|
||||||
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.
|
- **自动文档检测**:基于自定义分割模型 (LiteRT)
|
||||||
|
- **自动透视校正** + **图像增强** (OpenCV)
|
||||||
- Website: https://fairscan.org
|
- **快速 PDF 生成**,无需手动调整
|
||||||
- Blog: https://fairscan.org/blog/
|
- **完全离线** — 原版无网络权限
|
||||||
|
- **最小权限**,无跟踪,无广告
|
||||||
|
- **开源** GPLv3
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Contributing
|
## PC 服务器部署
|
||||||
|
|
||||||
Contributions are welcome, but please read the guidelines first: [CONTRIBUTING.md](CONTRIBUTING.md)
|
### 环境要求
|
||||||
|
|
||||||
---
|
| 组件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| Conda 环境 | `MinerU`(Python 3.10, PyTorch 2.6, CUDA 12.4) |
|
||||||
|
| MinerU | v3.0.9(markdown 处理) |
|
||||||
|
| OCRmyPDF | v15.4.4(ocrpdf 处理) |
|
||||||
|
| Tesseract | 5.5.2 + chi_sim / eng 语言包 |
|
||||||
|
| Ghostscript | 10.07.1 |
|
||||||
|
| GPU | NVIDIA RTX 4060 8GB(推荐) |
|
||||||
|
|
||||||
## Screenshots
|
### 启动
|
||||||
|
|
||||||
| Scan | Preview | Save & Share |
|
|
||||||
|------|---------|--------------|
|
|
||||||
|  |  |  |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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`。
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()}")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 ->
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user