tomo2chin2 commited on
Commit
055c32c
·
verified ·
1 Parent(s): ae4e5fc

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +252 -0
app.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import tempfile
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ from fastapi import FastAPI, HTTPException, File, UploadFile
9
+ from fastapi.responses import JSONResponse, RedirectResponse
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from pydantic import BaseModel
12
+ from playwright.sync_api import sync_playwright
13
+ from huggingface_hub import HfApi, upload_file
14
+
15
+
16
+ class HTMLRequest(BaseModel):
17
+ html_content: str
18
+
19
+
20
+ class PDFResponse(BaseModel):
21
+ pdf_url: str
22
+ filename: str
23
+ message: str
24
+ repository_url: str
25
+
26
+
27
+ def html_to_pdf_api(html_content: str) -> tuple[str, str]:
28
+ """
29
+ HTMLコンテンツをPDFに変換してHugging Faceデータセットリポジトリにアップロード
30
+ knowledge.txtのPlaywright手法を使用
31
+ """
32
+ try:
33
+ # 環境変数からリポジトリ情報を取得
34
+ hf_repo_id = os.getenv("HF_DATASET_REPO_ID")
35
+ hf_token = os.getenv("HF_TOKEN")
36
+
37
+ if not hf_repo_id:
38
+ raise HTTPException(status_code=500, detail="HF_DATASET_REPO_ID環境変数が設定されていません")
39
+ if not hf_token:
40
+ raise HTTPException(status_code=500, detail="HF_TOKEN環境変数が設定されていません")
41
+
42
+ # 一意のファイル名を生成
43
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
44
+ unique_id = str(uuid.uuid4())[:8]
45
+ filename = f"document_{timestamp}_{unique_id}.pdf"
46
+
47
+ # 一時ファイルでPDFを生成
48
+ with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as temp_file:
49
+ temp_path = temp_file.name
50
+
51
+ # Playwrightでヘッドレスブラウザを起動
52
+ with sync_playwright() as pw:
53
+ browser = pw.chromium.launch(headless=True)
54
+ page = browser.new_page()
55
+
56
+ # HTMLコンテンツを設定(外部リソース読み込み待機)
57
+ page.set_content(html_content, wait_until="networkidle")
58
+
59
+ # 印刷メディアを有効にする
60
+ page.emulate_media(media="print")
61
+
62
+ # PDFを生成(test.htmlの設定に準拠)
63
+ page.pdf(
64
+ path=temp_path,
65
+ format="A4",
66
+ print_background=True,
67
+ margin={"top":"15mm","bottom":"15mm","left":"15mm","right":"15mm"}
68
+ )
69
+
70
+ browser.close()
71
+
72
+ # Hugging Face リポジトリにアップロード
73
+ api = HfApi(token=hf_token)
74
+
75
+ upload_file(
76
+ path_or_fileobj=temp_path,
77
+ path_in_repo=f"pdfs/{filename}",
78
+ repo_id=hf_repo_id,
79
+ repo_type="dataset",
80
+ token=hf_token,
81
+ commit_message=f"Add PDF: {filename}"
82
+ )
83
+
84
+ # 一時ファイルを削除
85
+ os.unlink(temp_path)
86
+
87
+ # ダウンロードURLを生成
88
+ pdf_url = f"https://huggingface.co/datasets/{hf_repo_id}/resolve/main/pdfs/{filename}"
89
+
90
+ return filename, pdf_url
91
+
92
+ except HTTPException:
93
+ raise
94
+ except Exception as e:
95
+ raise HTTPException(status_code=500, detail=f"PDF生成中にエラーが発生しました: {str(e)}")
96
+
97
+
98
+ # FastAPIアプリケーションの初期化
99
+ app = FastAPI(
100
+ title="HTML to PDF Converter API",
101
+ description="日本語対応のHTML→PDF変換API。複雑なレイアウトやWebフォントを含むHTMLコンテンツを、正確にA4サイズのPDFに変換します。",
102
+ version="1.0.0",
103
+ docs_url="/docs",
104
+ redoc_url="/redoc"
105
+ )
106
+
107
+ # CORS設定
108
+ app.add_middleware(
109
+ CORSMiddleware,
110
+ allow_origins=["*"],
111
+ allow_credentials=True,
112
+ allow_methods=["*"],
113
+ allow_headers=["*"],
114
+ )
115
+
116
+
117
+ @app.get("/")
118
+ async def root():
119
+ """
120
+ API情報を返すルートエンドポイント
121
+ """
122
+ hf_repo_id = os.getenv("HF_DATASET_REPO_ID", "未設定")
123
+ return {
124
+ "message": "HTML to PDF Converter API",
125
+ "description": "HTMLコンテンツをA4サイズのPDFに変換し、Hugging Faceデータセットリポジトリに保存するAPI",
126
+ "version": "1.0.0",
127
+ "storage": f"Hugging Face Dataset Repository: {hf_repo_id}",
128
+ "endpoints": {
129
+ "convert": "/convert - HTMLをPDFに変換してHFリポジトリに保存",
130
+ "files": "/files - HFリポジトリ内のPDFファイル一覧",
131
+ "docs": "/docs - API仕様書",
132
+ "health": "/health - ヘルスチェック"
133
+ }
134
+ }
135
+
136
+
137
+ @app.post("/convert", response_model=PDFResponse)
138
+ async def convert_html_to_pdf(request: HTMLRequest):
139
+ """
140
+ HTMLコンテンツをPDFに変換してHugging Faceデータセットリポジトリに保存する���ンドポイント
141
+
142
+ - **html_content**: 変換するHTMLコンテンツ
143
+
144
+ Returns:
145
+ - **pdf_url**: 生成されたPDFのダウンロードURL(Hugging Face上)
146
+ - **filename**: PDFファイル名
147
+ - **message**: 処理結果メッセージ
148
+ - **repository_url**: リポジトリURL
149
+ """
150
+ if not request.html_content or not request.html_content.strip():
151
+ raise HTTPException(status_code=400, detail="HTMLコンテンツが空です")
152
+
153
+ try:
154
+ filename, pdf_url = html_to_pdf_api(request.html_content)
155
+ hf_repo_id = os.getenv("HF_DATASET_REPO_ID")
156
+ repository_url = f"https://huggingface.co/datasets/{hf_repo_id}"
157
+
158
+ return PDFResponse(
159
+ pdf_url=pdf_url,
160
+ filename=filename,
161
+ message=f"✅ PDFが正常に生成され、Hugging Faceリポジトリに保存されました: {filename}",
162
+ repository_url=repository_url
163
+ )
164
+ except HTTPException:
165
+ raise
166
+ except Exception as e:
167
+ raise HTTPException(status_code=500, detail=f"処理中にエラーが発生しました: {str(e)}")
168
+
169
+
170
+ @app.get("/download/{filename}")
171
+ async def download_pdf(filename: str):
172
+ """
173
+ Hugging FaceリポジトリのPDFファイルへリダイレクトするエンドポイント
174
+
175
+ - **filename**: ダウンロードするPDFファイル名
176
+ """
177
+ hf_repo_id = os.getenv("HF_DATASET_REPO_ID")
178
+ if not hf_repo_id:
179
+ raise HTTPException(status_code=500, detail="HF_DATASET_REPO_ID環境変数が設定されていません")
180
+
181
+ # Hugging Face上のファイルURLにリダイレクト
182
+ pdf_url = f"https://huggingface.co/datasets/{hf_repo_id}/resolve/main/pdfs/{filename}"
183
+ return RedirectResponse(url=pdf_url)
184
+
185
+
186
+ @app.get("/health")
187
+ async def health_check():
188
+ """
189
+ ヘルスチェックエンドポイント
190
+ """
191
+ return {"status": "healthy", "timestamp": datetime.now().isoformat()}
192
+
193
+
194
+ @app.get("/files")
195
+ async def list_files():
196
+ """
197
+ Hugging Faceリポジトリ内のPDFファイル一覧を取得するエンドポイント
198
+ """
199
+ try:
200
+ hf_repo_id = os.getenv("HF_DATASET_REPO_ID")
201
+ hf_token = os.getenv("HF_TOKEN")
202
+
203
+ if not hf_repo_id:
204
+ raise HTTPException(status_code=500, detail="HF_DATASET_REPO_ID環境変数が設定されていません")
205
+ if not hf_token:
206
+ raise HTTPException(status_code=500, detail="HF_TOKEN環境変数が設定されていません")
207
+
208
+ api = HfApi(token=hf_token)
209
+
210
+ # リポジトリ内のファイル一覧を取得
211
+ try:
212
+ repo_files = api.list_repo_files(repo_id=hf_repo_id, repo_type="dataset")
213
+ pdf_files = [f for f in repo_files if f.startswith("pdfs/") and f.endswith(".pdf")]
214
+
215
+ files = []
216
+ for file_path in pdf_files:
217
+ filename = Path(file_path).name
218
+ file_info = api.get_paths_info(repo_id=hf_repo_id, paths=[file_path], repo_type="dataset")[0]
219
+
220
+ files.append({
221
+ "filename": filename,
222
+ "path": file_path,
223
+ "size": file_info.size if hasattr(file_info, 'size') else 0,
224
+ "last_modified": file_info.last_commit.date.isoformat() if hasattr(file_info, 'last_commit') and file_info.last_commit else None,
225
+ "download_url": f"https://huggingface.co/datasets/{hf_repo_id}/resolve/main/{file_path}",
226
+ "api_download_url": f"/download/{filename}"
227
+ })
228
+
229
+ return {
230
+ "repository": hf_repo_id,
231
+ "total_files": len(files),
232
+ "files": files
233
+ }
234
+
235
+ except Exception as e:
236
+ # リポジトリが空の場合やpdfsフォルダが存在しない場合
237
+ return {
238
+ "repository": hf_repo_id,
239
+ "total_files": 0,
240
+ "files": [],
241
+ "note": "No PDF files found or repository is empty"
242
+ }
243
+
244
+ except HTTPException:
245
+ raise
246
+ except Exception as e:
247
+ raise HTTPException(status_code=500, detail=f"ファイル一覧取得中にエラーが発生しました: {str(e)}")
248
+
249
+
250
+ if __name__ == "__main__":
251
+ import uvicorn
252
+ uvicorn.run(app, host="0.0.0.0", port=7860)