Update app.py
Browse files
app.py
CHANGED
|
@@ -9,6 +9,13 @@ import threading
|
|
| 9 |
import concurrent.futures
|
| 10 |
from openai import OpenAI
|
| 11 |
import fitz # PyMuPDF
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
# λ‘κΉ
μ€μ
|
| 14 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
@@ -948,6 +955,199 @@ async def upload_pdf(file: UploadFile = File(...)):
|
|
| 948 |
status_code=500
|
| 949 |
)
|
| 950 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 951 |
# κ΄λ¦¬μ μΈμ¦ μλν¬μΈνΈ
|
| 952 |
@app.post("/api/admin-login")
|
| 953 |
async def admin_login(password: str = Form(...)):
|
|
@@ -963,9 +1163,17 @@ async def delete_pdf(path: str):
|
|
| 963 |
if not pdf_file.exists():
|
| 964 |
return {"success": False, "message": "νμΌμ μ°Ύμ μ μμ΅λλ€"}
|
| 965 |
|
| 966 |
-
# PDF νμΌ
|
|
|
|
|
|
|
|
|
|
| 967 |
pdf_file.unlink()
|
| 968 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 969 |
# κ΄λ ¨ μΊμ νμΌ μμ
|
| 970 |
pdf_name = pdf_file.stem
|
| 971 |
cache_path = get_cache_path(pdf_name)
|
|
@@ -979,7 +1187,7 @@ async def delete_pdf(path: str):
|
|
| 979 |
# λ©νλ°μ΄ν°μμ ν΄λΉ νμΌ ID μ κ±°
|
| 980 |
to_remove = []
|
| 981 |
for pid, fpath in pdf_metadata.items():
|
| 982 |
-
if os.path.basename(fpath) ==
|
| 983 |
to_remove.append(pid)
|
| 984 |
|
| 985 |
for pid in to_remove:
|
|
@@ -1351,7 +1559,9 @@ HTML = """
|
|
| 1351 |
font-weight: 500;
|
| 1352 |
display: flex;
|
| 1353 |
align-items: center;
|
| 1354 |
-
|
|
|
|
|
|
|
| 1355 |
transition: var(--transition);
|
| 1356 |
position: relative;
|
| 1357 |
overflow: hidden;
|
|
@@ -2111,7 +2321,11 @@ HTML = """
|
|
| 2111 |
<button class="upload" id="pdfUploadBtn">
|
| 2112 |
<i class="fas fa-file-pdf"></i> PDF Upload
|
| 2113 |
</button>
|
|
|
|
|
|
|
|
|
|
| 2114 |
<input id="pdfInput" type="file" accept="application/pdf" style="display:none">
|
|
|
|
| 2115 |
</div>
|
| 2116 |
|
| 2117 |
<div class="section-title">Projects</div>
|
|
@@ -2436,9 +2650,6 @@ async function submitQuestion(question) {
|
|
| 2436 |
}
|
| 2437 |
}
|
| 2438 |
|
| 2439 |
-
|
| 2440 |
-
|
| 2441 |
-
|
| 2442 |
|
| 2443 |
// DOMμ΄ λ‘λλλ©΄ μ€ν
|
| 2444 |
document.addEventListener('DOMContentLoaded', function() {
|
|
@@ -2473,6 +2684,26 @@ async function submitQuestion(question) {
|
|
| 2473 |
console.error("PDF μ
λ‘λ μμλ₯Ό μ°Ύμ μ μμ");
|
| 2474 |
}
|
| 2475 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2476 |
// μλ² PDF λ‘λ λ° μΊμ μν νμΈ
|
| 2477 |
loadServerPDFs();
|
| 2478 |
|
|
@@ -2573,6 +2804,40 @@ async function submitQuestion(question) {
|
|
| 2573 |
}
|
| 2574 |
}
|
| 2575 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2576 |
function addCard(i, thumb, title, isCached = false, pdfId = null) {
|
| 2577 |
const d = document.createElement('div');
|
| 2578 |
d.className = 'card fade-in';
|
|
@@ -3636,7 +3901,7 @@ async function submitQuestion(question) {
|
|
| 3636 |
}
|
| 3637 |
</script>
|
| 3638 |
</body>
|
| 3639 |
-
</html>
|
| 3640 |
"""
|
| 3641 |
|
| 3642 |
if __name__ == "__main__":
|
|
|
|
| 9 |
import concurrent.futures
|
| 10 |
from openai import OpenAI
|
| 11 |
import fitz # PyMuPDF
|
| 12 |
+
import tempfile
|
| 13 |
+
from reportlab.lib.pagesizes import letter
|
| 14 |
+
from reportlab.pdfgen import canvas
|
| 15 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
|
| 16 |
+
from reportlab.lib.styles import getSampleStyleSheet
|
| 17 |
+
import io
|
| 18 |
+
import docx2txt
|
| 19 |
|
| 20 |
# λ‘κΉ
μ€μ
|
| 21 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
|
|
| 955 |
status_code=500
|
| 956 |
)
|
| 957 |
|
| 958 |
+
# ν
μ€νΈ νμΌμ PDFλ‘ λ³ννλ ν¨μ
|
| 959 |
+
async def convert_text_to_pdf(text_content: str, title: str) -> str:
|
| 960 |
+
try:
|
| 961 |
+
# μ λͺ©μμ μ ν¨ν νμΌλͺ
μμ±
|
| 962 |
+
import re
|
| 963 |
+
safe_title = re.sub(r'[^\w\-_\. ]', '_', title)
|
| 964 |
+
if not safe_title:
|
| 965 |
+
safe_title = "aibook"
|
| 966 |
+
|
| 967 |
+
# νμμ€ν¬ν μΆκ°λ‘ κ³ μ ν νμΌλͺ
μμ±
|
| 968 |
+
timestamp = int(time.time())
|
| 969 |
+
filename = f"{safe_title}_{timestamp}.pdf"
|
| 970 |
+
|
| 971 |
+
# μꡬ μ μ₯μμ νμΌ κ²½λ‘
|
| 972 |
+
file_path = PERMANENT_PDF_DIR / filename
|
| 973 |
+
|
| 974 |
+
# νκΈ ν°νΈ λ±λ‘ - μ
λ‘λλ MaruBuri-SemiBold.ttf μ¬μ©
|
| 975 |
+
from reportlab.pdfbase import pdfmetrics
|
| 976 |
+
from reportlab.pdfbase.ttfonts import TTFont
|
| 977 |
+
|
| 978 |
+
# ν°νΈ κ²½λ‘ μ€μ (app.pyμ κ°μ λλ ν 리μ μλ ν°νΈ μ¬μ©)
|
| 979 |
+
font_path = BASE / "MaruBuri-SemiBold.ttf"
|
| 980 |
+
|
| 981 |
+
# ν°νΈ λ±λ‘
|
| 982 |
+
font_name = "MaruBuri"
|
| 983 |
+
if font_path.exists():
|
| 984 |
+
pdfmetrics.registerFont(TTFont(font_name, str(font_path)))
|
| 985 |
+
logger.info(f"νκΈ ν°νΈ λ±λ‘ μ±κ³΅: {font_path}")
|
| 986 |
+
else:
|
| 987 |
+
font_name = "Helvetica"
|
| 988 |
+
logger.warning(f"νκΈ ν°νΈ νμΌμ μ°Ύμ μ μμ΅λλ€: {font_path}. κΈ°λ³Έ ν°νΈλ₯Ό μ¬μ©ν©λλ€.")
|
| 989 |
+
|
| 990 |
+
# μμ PDF νμΌ μμ±
|
| 991 |
+
pdf_buffer = io.BytesIO()
|
| 992 |
+
|
| 993 |
+
# νκΈ μ§μμ μν μ€νμΌ μ€μ
|
| 994 |
+
from reportlab.lib.pagesizes import letter
|
| 995 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
|
| 996 |
+
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 997 |
+
from reportlab.lib.enums import TA_CENTER, TA_LEFT
|
| 998 |
+
|
| 999 |
+
doc = SimpleDocTemplate(pdf_buffer, pagesize=letter, encoding='utf-8')
|
| 1000 |
+
|
| 1001 |
+
# μ¬μ©μ μ μ μ€νμΌ μμ±
|
| 1002 |
+
title_style = ParagraphStyle(
|
| 1003 |
+
name='CustomTitle',
|
| 1004 |
+
fontName=font_name,
|
| 1005 |
+
fontSize=18,
|
| 1006 |
+
leading=22,
|
| 1007 |
+
alignment=TA_CENTER,
|
| 1008 |
+
spaceAfter=20
|
| 1009 |
+
)
|
| 1010 |
+
|
| 1011 |
+
normal_style = ParagraphStyle(
|
| 1012 |
+
name='CustomNormal',
|
| 1013 |
+
fontName=font_name,
|
| 1014 |
+
fontSize=12,
|
| 1015 |
+
leading=15,
|
| 1016 |
+
alignment=TA_LEFT,
|
| 1017 |
+
spaceBefore=6,
|
| 1018 |
+
spaceAfter=6
|
| 1019 |
+
)
|
| 1020 |
+
|
| 1021 |
+
# λ΄μ©μ λ¬Έλ¨μΌλ‘ λΆν
|
| 1022 |
+
content = []
|
| 1023 |
+
|
| 1024 |
+
# μ λͺ© μΆκ°
|
| 1025 |
+
content.append(Paragraph(title, title_style))
|
| 1026 |
+
content.append(Spacer(1, 20))
|
| 1027 |
+
|
| 1028 |
+
# ν
μ€νΈλ₯Ό λ¨λ½μΌλ‘ λΆλ¦¬νμ¬ μΆκ°
|
| 1029 |
+
paragraphs = text_content.split('\n\n')
|
| 1030 |
+
for para in paragraphs:
|
| 1031 |
+
if para.strip():
|
| 1032 |
+
# XML νΉμλ¬Έμ μ΄μ€μΌμ΄ν μ²λ¦¬
|
| 1033 |
+
from xml.sax.saxutils import escape
|
| 1034 |
+
safe_para = escape(para.replace('\n', '<br/>'))
|
| 1035 |
+
p = Paragraph(safe_para, normal_style)
|
| 1036 |
+
content.append(p)
|
| 1037 |
+
content.append(Spacer(1, 10))
|
| 1038 |
+
|
| 1039 |
+
# PDF μμ±
|
| 1040 |
+
doc.build(content)
|
| 1041 |
+
|
| 1042 |
+
# νμΌλ‘ μ μ₯
|
| 1043 |
+
with open(file_path, 'wb') as f:
|
| 1044 |
+
f.write(pdf_buffer.getvalue())
|
| 1045 |
+
|
| 1046 |
+
# λ©μΈ λλ ν 리μλ 볡μ¬
|
| 1047 |
+
with open(PDF_DIR / filename, 'wb') as f:
|
| 1048 |
+
f.write(pdf_buffer.getvalue())
|
| 1049 |
+
|
| 1050 |
+
# PDF ID μμ± λ° λ©νλ°μ΄ν° μ μ₯
|
| 1051 |
+
pdf_id = generate_pdf_id(filename)
|
| 1052 |
+
pdf_metadata[pdf_id] = str(file_path)
|
| 1053 |
+
save_pdf_metadata()
|
| 1054 |
+
|
| 1055 |
+
# λ°±κ·ΈλΌμ΄λμμ μΊμ± μμ
|
| 1056 |
+
asyncio.create_task(cache_pdf(str(file_path)))
|
| 1057 |
+
|
| 1058 |
+
return {
|
| 1059 |
+
"path": str(file_path),
|
| 1060 |
+
"filename": filename,
|
| 1061 |
+
"id": pdf_id
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
except Exception as e:
|
| 1065 |
+
logger.error(f"ν
μ€νΈλ₯Ό PDFλ‘ λ³ν μ€ μ€λ₯: {e}")
|
| 1066 |
+
raise e
|
| 1067 |
+
|
| 1068 |
+
|
| 1069 |
+
# AIλ₯Ό μ¬μ©νμ¬ ν
μ€νΈλ₯Ό λ ꡬ쑰νλ νμμΌλ‘ λ³ν (OpenAI μ κ±° λ²μ )
|
| 1070 |
+
async def enhance_text_with_ai(text_content: str, title: str) -> str:
|
| 1071 |
+
# μλ³Έ ν
μ€νΈ κ·Έλλ‘ λ°ν (AI ν₯μ κΈ°λ₯ λΉνμ±ν)
|
| 1072 |
+
return text_content
|
| 1073 |
+
|
| 1074 |
+
|
| 1075 |
+
|
| 1076 |
+
# ν
μ€νΈ νμΌμ PDFλ‘ λ³ννλ μλν¬μΈνΈ
|
| 1077 |
+
@app.post("/api/text-to-pdf")
|
| 1078 |
+
async def text_to_pdf(file: UploadFile = File(...)):
|
| 1079 |
+
try:
|
| 1080 |
+
# μ§μνλ νμΌ νμ νμΈ
|
| 1081 |
+
filename = file.filename.lower()
|
| 1082 |
+
if not (filename.endswith('.txt') or filename.endswith('.docx') or filename.endswith('.doc')):
|
| 1083 |
+
return JSONResponse(
|
| 1084 |
+
content={"success": False, "message": "μ§μνλ νμΌ νμμ .txt, .docx, .docμ
λλ€."},
|
| 1085 |
+
status_code=400
|
| 1086 |
+
)
|
| 1087 |
+
|
| 1088 |
+
# νμΌ λ΄μ© μ½κΈ°
|
| 1089 |
+
content = await file.read()
|
| 1090 |
+
|
| 1091 |
+
# νμΌ νμ
μ λ°λΌ ν
μ€νΈ μΆμΆ
|
| 1092 |
+
if filename.endswith('.txt'):
|
| 1093 |
+
# μΈμ½λ© μλ κ°μ§ μλ
|
| 1094 |
+
encodings = ['utf-8', 'euc-kr', 'cp949', 'latin1']
|
| 1095 |
+
text_content = None
|
| 1096 |
+
|
| 1097 |
+
for encoding in encodings:
|
| 1098 |
+
try:
|
| 1099 |
+
text_content = content.decode(encoding, errors='strict')
|
| 1100 |
+
logger.info(f"ν
μ€νΈ νμΌ μΈμ½λ© κ°μ§: {encoding}")
|
| 1101 |
+
break
|
| 1102 |
+
except UnicodeDecodeError:
|
| 1103 |
+
continue
|
| 1104 |
+
|
| 1105 |
+
if text_content is None:
|
| 1106 |
+
# λͺ¨λ μΈμ½λ© μλ μ€ν¨ μ κΈ°λ³Έμ μΌλ‘ UTF-8λ‘ μλνκ³ μ€λ₯λ λ체 λ¬Έμλ‘ μ²λ¦¬
|
| 1107 |
+
text_content = content.decode('utf-8', errors='replace')
|
| 1108 |
+
logger.warning("ν
μ€νΈ νμΌ μΈμ½λ©μ κ°μ§ν μ μμ΄ UTF-8μΌλ‘ μλν©λλ€.")
|
| 1109 |
+
|
| 1110 |
+
elif filename.endswith('.docx') or filename.endswith('.doc'):
|
| 1111 |
+
# μμ νμΌλ‘ μ μ₯
|
| 1112 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(filename)[1]) as temp_file:
|
| 1113 |
+
temp_file.write(content)
|
| 1114 |
+
temp_path = temp_file.name
|
| 1115 |
+
|
| 1116 |
+
try:
|
| 1117 |
+
# docx2txtλ‘ ν
μ€νΈ μΆμΆ
|
| 1118 |
+
text_content = docx2txt.process(temp_path)
|
| 1119 |
+
finally:
|
| 1120 |
+
# μμ νμΌ μμ
|
| 1121 |
+
os.unlink(temp_path)
|
| 1122 |
+
|
| 1123 |
+
# νμΌλͺ
μμ μ λͺ© μΆμΆ (νμ₯μ μ μΈ)
|
| 1124 |
+
title = os.path.splitext(filename)[0]
|
| 1125 |
+
|
| 1126 |
+
# AIλ‘ ν
μ€νΈ λ΄μ© ν₯μ
|
| 1127 |
+
enhanced_text = await enhance_text_with_ai(text_content, title)
|
| 1128 |
+
|
| 1129 |
+
# ν
μ€νΈλ₯Ό PDFλ‘ λ³ν
|
| 1130 |
+
pdf_info = await convert_text_to_pdf(enhanced_text, title)
|
| 1131 |
+
|
| 1132 |
+
return JSONResponse(
|
| 1133 |
+
content={
|
| 1134 |
+
"success": True,
|
| 1135 |
+
"path": pdf_info["path"],
|
| 1136 |
+
"name": os.path.splitext(pdf_info["filename"])[0],
|
| 1137 |
+
"id": pdf_info["id"],
|
| 1138 |
+
"viewUrl": f"/view/{pdf_info['id']}"
|
| 1139 |
+
},
|
| 1140 |
+
status_code=200
|
| 1141 |
+
)
|
| 1142 |
+
except Exception as e:
|
| 1143 |
+
import traceback
|
| 1144 |
+
error_details = traceback.format_exc()
|
| 1145 |
+
logger.error(f"ν
μ€νΈλ₯Ό PDFλ‘ λ³ν μ€ μ€λ₯: {str(e)}\n{error_details}")
|
| 1146 |
+
return JSONResponse(
|
| 1147 |
+
content={"success": False, "message": str(e)},
|
| 1148 |
+
status_code=500
|
| 1149 |
+
)
|
| 1150 |
+
|
| 1151 |
# κ΄λ¦¬μ μΈμ¦ μλν¬μΈνΈ
|
| 1152 |
@app.post("/api/admin-login")
|
| 1153 |
async def admin_login(password: str = Form(...)):
|
|
|
|
| 1163 |
if not pdf_file.exists():
|
| 1164 |
return {"success": False, "message": "νμΌμ μ°Ύμ μ μμ΅λλ€"}
|
| 1165 |
|
| 1166 |
+
# PDF νμΌλͺ
κ°μ Έμ€κΈ°
|
| 1167 |
+
filename = pdf_file.name
|
| 1168 |
+
|
| 1169 |
+
# PDF νμΌ μμ (μꡬ μ μ₯μμμ)
|
| 1170 |
pdf_file.unlink()
|
| 1171 |
|
| 1172 |
+
# λ©μΈ λλ ν 리μμλ λμΌν νμΌμ΄ μμΌλ©΄ μμ (λ²κ·Έ μμ )
|
| 1173 |
+
main_file_path = PDF_DIR / filename
|
| 1174 |
+
if main_file_path.exists():
|
| 1175 |
+
main_file_path.unlink()
|
| 1176 |
+
|
| 1177 |
# κ΄λ ¨ μΊμ νμΌ μμ
|
| 1178 |
pdf_name = pdf_file.stem
|
| 1179 |
cache_path = get_cache_path(pdf_name)
|
|
|
|
| 1187 |
# λ©νλ°μ΄ν°μμ ν΄λΉ νμΌ ID μ κ±°
|
| 1188 |
to_remove = []
|
| 1189 |
for pid, fpath in pdf_metadata.items():
|
| 1190 |
+
if os.path.basename(fpath) == filename:
|
| 1191 |
to_remove.append(pid)
|
| 1192 |
|
| 1193 |
for pid in to_remove:
|
|
|
|
| 1559 |
font-weight: 500;
|
| 1560 |
display: flex;
|
| 1561 |
align-items: center;
|
| 1562 |
+
|
| 1563 |
+
|
| 1564 |
+
box-shadow: var(--shadow-sm);
|
| 1565 |
transition: var(--transition);
|
| 1566 |
position: relative;
|
| 1567 |
overflow: hidden;
|
|
|
|
| 2321 |
<button class="upload" id="pdfUploadBtn">
|
| 2322 |
<i class="fas fa-file-pdf"></i> PDF Upload
|
| 2323 |
</button>
|
| 2324 |
+
<button class="upload" id="textToAIBookBtn">
|
| 2325 |
+
<i class="fas fa-file-alt"></i> Text to AI-Book
|
| 2326 |
+
</button>
|
| 2327 |
<input id="pdfInput" type="file" accept="application/pdf" style="display:none">
|
| 2328 |
+
<input id="textInput" type="file" accept=".txt,.docx,.doc" style="display:none">
|
| 2329 |
</div>
|
| 2330 |
|
| 2331 |
<div class="section-title">Projects</div>
|
|
|
|
| 2650 |
}
|
| 2651 |
}
|
| 2652 |
|
|
|
|
|
|
|
|
|
|
| 2653 |
|
| 2654 |
// DOMμ΄ λ‘λλλ©΄ μ€ν
|
| 2655 |
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
| 2684 |
console.error("PDF μ
λ‘λ μμλ₯Ό μ°Ύμ μ μμ");
|
| 2685 |
}
|
| 2686 |
|
| 2687 |
+
// ν
μ€νΈ μ
λ‘λ λ²νΌ
|
| 2688 |
+
const textBtn = document.getElementById('textToAIBookBtn');
|
| 2689 |
+
const textInput = document.getElementById('textInput');
|
| 2690 |
+
|
| 2691 |
+
if (textBtn && textInput) {
|
| 2692 |
+
// λ²νΌ ν΄λ¦ μ νμΌ μ
λ ₯ νΈλ¦¬κ±°
|
| 2693 |
+
textBtn.addEventListener('click', function() {
|
| 2694 |
+
textInput.click();
|
| 2695 |
+
});
|
| 2696 |
+
|
| 2697 |
+
// νμΌ μ ν μ μ²λ¦¬
|
| 2698 |
+
textInput.addEventListener('change', function(e) {
|
| 2699 |
+
const file = e.target.files[0];
|
| 2700 |
+
if (!file) return;
|
| 2701 |
+
|
| 2702 |
+
// μλ²μ ν
μ€νΈ νμΌ μ
λ‘λ (μꡬ μ μ₯μμ PDFλ‘ λ³ννμ¬ μ μ₯)
|
| 2703 |
+
uploadTextToServer(file);
|
| 2704 |
+
});
|
| 2705 |
+
}
|
| 2706 |
+
|
| 2707 |
// μλ² PDF λ‘λ λ° μΊμ μν νμΈ
|
| 2708 |
loadServerPDFs();
|
| 2709 |
|
|
|
|
| 2804 |
}
|
| 2805 |
}
|
| 2806 |
|
| 2807 |
+
// μλ²μ ν
μ€νΈ νμΌμ μ
λ‘λνμ¬ PDFλ‘ λ³ννλ ν¨μ
|
| 2808 |
+
async function uploadTextToServer(file) {
|
| 2809 |
+
try {
|
| 2810 |
+
showLoading("ν
μ€νΈ λΆμ λ° PDF λ³ν μ€...");
|
| 2811 |
+
|
| 2812 |
+
const formData = new FormData();
|
| 2813 |
+
formData.append('file', file);
|
| 2814 |
+
|
| 2815 |
+
const response = await fetch('/api/text-to-pdf', {
|
| 2816 |
+
method: 'POST',
|
| 2817 |
+
body: formData
|
| 2818 |
+
});
|
| 2819 |
+
|
| 2820 |
+
const result = await response.json();
|
| 2821 |
+
|
| 2822 |
+
if (result.success) {
|
| 2823 |
+
hideLoading();
|
| 2824 |
+
|
| 2825 |
+
// μ
λ‘λ μ±κ³΅ μ μλ² PDF 리μ€νΈ 리λ‘λ
|
| 2826 |
+
await loadServerPDFs();
|
| 2827 |
+
|
| 2828 |
+
// μ±κ³΅ λ©μμ§
|
| 2829 |
+
showMessage("ν
μ€νΈκ° μ±κ³΅μ μΌλ‘ PDFλ‘ λ³νλμμ΅λλ€! 곡μ URL: " + result.viewUrl);
|
| 2830 |
+
} else {
|
| 2831 |
+
hideLoading();
|
| 2832 |
+
showError("λ³ν μ€ν¨: " + (result.message || "μ μ μλ μ€λ₯"));
|
| 2833 |
+
}
|
| 2834 |
+
} catch (error) {
|
| 2835 |
+
console.error("ν
μ€νΈ λ³ν μ€λ₯:", error);
|
| 2836 |
+
hideLoading();
|
| 2837 |
+
showError("ν
μ€νΈλ₯Ό PDFλ‘ λ³ννλ μ€ μ€λ₯κ° λ°μνμ΅λλ€.");
|
| 2838 |
+
}
|
| 2839 |
+
}
|
| 2840 |
+
|
| 2841 |
function addCard(i, thumb, title, isCached = false, pdfId = null) {
|
| 2842 |
const d = document.createElement('div');
|
| 2843 |
d.className = 'card fade-in';
|
|
|
|
| 3901 |
}
|
| 3902 |
</script>
|
| 3903 |
</body>
|
| 3904 |
+
</html>
|
| 3905 |
"""
|
| 3906 |
|
| 3907 |
if __name__ == "__main__":
|