Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Update app.py
Browse files
app.py
CHANGED
@@ -206,7 +206,7 @@ async def cache_pdf(pdf_path: str):
|
|
206 |
except Exception as e:
|
207 |
import traceback
|
208 |
logger.error(f"PDF ์บ์ฑ ์ค๋ฅ: {str(e)}\n{traceback.format_exc()}")
|
209 |
-
if pdf_name in pdf_cache:
|
210 |
pdf_cache[pdf_name]["status"] = "error"
|
211 |
pdf_cache[pdf_name]["error"] = str(e)
|
212 |
|
@@ -310,7 +310,6 @@ async def get_cached_pdf(path: str, background_tasks: BackgroundTasks):
|
|
310 |
pages = pdf_cache[pdf_name].get("pages", [])
|
311 |
total_pages = pdf_cache[pdf_name].get("total_pages", 0)
|
312 |
|
313 |
-
# ์ผ๋ถ๋ง ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉ ๊ฐ๋ฅํ ํ์ด์ง ์ ๊ณต
|
314 |
return {
|
315 |
"status": "processing",
|
316 |
"progress": progress,
|
@@ -417,11 +416,11 @@ HTML = """
|
|
417 |
|
418 |
body {
|
419 |
margin: 0;
|
420 |
-
|
|
|
|
|
421 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
422 |
color: var(--text-color);
|
423 |
-
background-image: linear-gradient(120deg, var(--tertiary-color) 0%, var(--bg-color) 100%);
|
424 |
-
background-attachment: fixed;
|
425 |
}
|
426 |
|
427 |
/* ํค๋ ์ ๋ชฉ ์ ๊ฑฐ ๋ฐ Home ๋ฒํผ ๋ ์ด์ด ์ฒ๋ฆฌ */
|
@@ -484,7 +483,7 @@ HTML = """
|
|
484 |
opacity: 1;
|
485 |
transform: translateX(0);
|
486 |
}
|
487 |
-
|
488 |
#home, #viewerPage {
|
489 |
padding-top: 100px;
|
490 |
max-width: 1200px;
|
@@ -510,7 +509,7 @@ HTML = """
|
|
510 |
background: white;
|
511 |
margin: 0 10px;
|
512 |
font-weight: 500;
|
513 |
-
display: flex;
|
514 |
align-items: center;
|
515 |
box-shadow: var(--shadow-sm);
|
516 |
transition: var(--transition);
|
@@ -525,7 +524,7 @@ HTML = """
|
|
525 |
left: 0;
|
526 |
width: 100%;
|
527 |
height: 100%;
|
528 |
-
background: linear-gradient(120deg, var(--primary-color)
|
529 |
opacity: 0.08;
|
530 |
z-index: -1;
|
531 |
}
|
@@ -801,30 +800,30 @@ HTML = """
|
|
801 |
}
|
802 |
|
803 |
/* ํค๋ ๋ก๊ณ ๋ฐ ํ์ดํ */
|
|
|
804 |
.library-header {
|
805 |
position: fixed;
|
806 |
-
top: 20px
|
807 |
left: 0;
|
808 |
right: 0;
|
809 |
text-align: center;
|
810 |
z-index: 100;
|
811 |
-
pointer-events: none;
|
812 |
}
|
813 |
|
814 |
.library-header .title {
|
815 |
display: inline-block;
|
816 |
-
|
|
|
817 |
background: rgba(255, 255, 255, 0.85);
|
818 |
backdrop-filter: blur(10px);
|
819 |
border-radius: 30px;
|
820 |
box-shadow: var(--shadow-md);
|
821 |
-
font-size: 1.5rem
|
822 |
font-weight: 600;
|
823 |
background-image: linear-gradient(120deg, #667eea 0%, #764ba2 100%);
|
824 |
-webkit-background-clip: text;
|
825 |
background-clip: text;
|
826 |
color: transparent;
|
827 |
-
pointer-events: all;
|
828 |
}
|
829 |
|
830 |
/* ์ ์ง์ ๋ก๋ฉ ํ์ */
|
@@ -855,8 +854,8 @@ HTML = """
|
|
855 |
}
|
856 |
|
857 |
.library-header .title {
|
858 |
-
font-size:
|
859 |
-
padding:
|
860 |
}
|
861 |
|
862 |
.floating-home {
|
@@ -884,18 +883,16 @@ HTML = """
|
|
884 |
|
885 |
<section id="home" class="fade-in">
|
886 |
<div class="upload-container">
|
887 |
-
<button
|
888 |
<i class="fas fa-images"></i> ์ด๋ฏธ์ง ์ถ๊ฐ
|
|
|
889 |
</button>
|
890 |
-
<button
|
891 |
<i class="fas fa-file-pdf"></i> PDF ์ถ๊ฐ
|
|
|
892 |
</button>
|
893 |
</div>
|
894 |
|
895 |
-
<!-- ํ์ผ ์
๋ ฅ ์์๋ฅผ ๋ฒํผ ๋ฐ์ผ๋ก ์ด๋ํ์ฌ ๋ณ๋ ๋ฐฐ์น -->
|
896 |
-
<input id="imgInput" type="file" accept="image/*" multiple style="display:none">
|
897 |
-
<input id="pdfInput" type="file" accept="application/pdf" style="display:none">
|
898 |
-
|
899 |
<div class="section-title">๋ด ํ๋ก์ ํธ</div>
|
900 |
<div class="grid" id="grid">
|
901 |
<!-- ์นด๋๊ฐ ์ฌ๊ธฐ์ ๋์ ์ผ๋ก ์ถ๊ฐ๋ฉ๋๋ค -->
|
@@ -928,133 +925,104 @@ HTML = """
|
|
928 |
.play().then(a=>a.pause()).catch(()=>{});document.removeEventListener(evt,u,{capture:true});},
|
929 |
{once:true,capture:true});
|
930 |
});
|
931 |
-
|
932 |
-
|
933 |
-
document.
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
if (
|
941 |
-
console.log('์ด๋ฏธ์ง
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
console.error('์ด๋ฏธ์ง ์
๋ก๋ ๋ฒํผ ๋๋ ์
๋ ฅ ์์๋ฅผ ์ฐพ์ ์ ์์');
|
948 |
}
|
949 |
|
950 |
-
// PDF ์
๋ก๋ ๋ฒํผ
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
pdfInput.click();
|
959 |
-
}
|
960 |
-
} else {
|
961 |
-
console.error('PDF ์
๋ก๋ ๋ฒํผ ๋๋ ์
๋ ฅ ์์๋ฅผ ์ฐพ์ ์ ์์');
|
962 |
-
}
|
963 |
-
|
964 |
-
console.log('PDF/์ด๋ฏธ์ง ์
๋ ฅ ํ๋ ์ด๋ฒคํธ ์ค์ ');
|
965 |
-
if (imgInput) {
|
966 |
-
imgInput.addEventListener('change', function(e) {
|
967 |
-
console.log('์ด๋ฏธ์ง ์
๋ ฅ ๋ณ๊ฒฝ ๊ฐ์ง๋จ');
|
968 |
-
handleImageUpload(e);
|
969 |
-
});
|
970 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
971 |
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
handlePdfUpload(e);
|
976 |
-
});
|
977 |
-
}
|
978 |
|
979 |
-
|
980 |
-
|
981 |
-
if (homeButton) {
|
982 |
-
homeButton.addEventListener('click', function() {
|
983 |
-
console.log('ํ ๋ฒํผ ํด๋ฆญ๋จ');
|
984 |
-
homeButtonClicked();
|
985 |
-
});
|
986 |
-
}
|
987 |
|
988 |
-
|
989 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
990 |
|
991 |
-
//
|
992 |
-
|
993 |
-
}
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
const files =
|
998 |
-
if
|
999 |
|
1000 |
-
// ๋ก๋ฉ ํ์ ์ถ๊ฐ
|
1001 |
showLoading("์ด๋ฏธ์ง ๋ก๋ฉ ์ค...");
|
1002 |
|
1003 |
-
const pages =
|
1004 |
let done = 0;
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
pages[index] = {
|
1011 |
-
src: event.target.result,
|
1012 |
-
thumb: event.target.result
|
1013 |
-
};
|
1014 |
-
done++;
|
1015 |
-
if (done === total) {
|
1016 |
save(pages, '์ด๋ฏธ์ง ์ปฌ๋ ์
');
|
1017 |
hideLoading();
|
1018 |
}
|
1019 |
};
|
1020 |
-
|
1021 |
});
|
1022 |
-
}
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
const file = e.target.files[0];
|
1027 |
-
if
|
1028 |
|
1029 |
-
// ๋ก๋ฉ ํ์ ์ถ๊ฐ
|
1030 |
showLoading("PDF ๋ก๋ฉ ์ค...");
|
1031 |
|
1032 |
-
const
|
1033 |
-
|
1034 |
-
pdfjsLib.getDocument({data:
|
1035 |
const pages = [];
|
1036 |
|
1037 |
-
for
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
canvas.width = viewport.width;
|
1045 |
-
canvas.height = viewport.height;
|
1046 |
-
|
1047 |
-
await page.render({
|
1048 |
-
canvasContext: canvas.getContext('2d'),
|
1049 |
-
viewport: viewport
|
1050 |
-
}).promise;
|
1051 |
-
|
1052 |
-
pages.push({
|
1053 |
-
src: canvas.toDataURL(),
|
1054 |
-
thumb: canvas.toDataURL()
|
1055 |
-
});
|
1056 |
}
|
1057 |
-
|
1058 |
hideLoading();
|
1059 |
save(pages, file.name.replace('.pdf', ''));
|
1060 |
}).catch(error => {
|
@@ -1063,56 +1031,9 @@ HTML = """
|
|
1063 |
showError("PDF ๋ก๋ฉ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
1064 |
});
|
1065 |
};
|
1066 |
-
|
1067 |
-
}
|
1068 |
-
|
1069 |
-
// ํ ๋ฒํผ ํด๋ฆญ ์ฒ๋ฆฌ ํจ์
|
1070 |
-
function homeButtonClicked() {
|
1071 |
-
if (fb) {
|
1072 |
-
fb.destroy();
|
1073 |
-
viewer.innerHTML = '';
|
1074 |
-
fb = null;
|
1075 |
-
}
|
1076 |
-
toggle(true);
|
1077 |
-
|
1078 |
-
// ๋ก๋ฉ ์ธ๋์ผ์ดํฐ ์ ๋ฆฌ
|
1079 |
-
if (pageLoadingInterval) {
|
1080 |
-
clearInterval(pageLoadingInterval);
|
1081 |
-
pageLoadingInterval = null;
|
1082 |
-
}
|
1083 |
-
document.getElementById('loadingPages').style.display = 'none';
|
1084 |
-
currentLoadingPdfPath = null;
|
1085 |
-
}
|
1086 |
-
const d = document.createElement('div');
|
1087 |
-
d.className = 'card fade-in';
|
1088 |
-
d.onclick = () => open(i);
|
1089 |
-
|
1090 |
-
// ์ ๋ชฉ ์ฒ๋ฆฌ
|
1091 |
-
const displayTitle = title ?
|
1092 |
-
(title.length > 15 ? title.substring(0, 15) + '...' : title) :
|
1093 |
-
'ํ๋ก์ ํธ ' + (i+1);
|
1094 |
-
|
1095 |
-
// ์บ์ ์ํ ๋ฑ์ง ์ถ๊ฐ
|
1096 |
-
const cachedBadge = isCached ?
|
1097 |
-
'<div class="cached-status">์บ์๋จ</div>' : '';
|
1098 |
-
|
1099 |
-
d.innerHTML = `
|
1100 |
-
<div class="card-inner">
|
1101 |
-
${cachedBadge}
|
1102 |
-
<img src="${thumb}" alt="${displayTitle}" loading="lazy">
|
1103 |
-
<p title="${title || 'ํ๋ก์ ํธ ' + (i+1)}">${displayTitle}</p>
|
1104 |
-
</div>
|
1105 |
-
`;
|
1106 |
-
grid.appendChild(d);
|
1107 |
-
|
1108 |
-
// ํ๋ก์ ํธ๊ฐ ์์ผ๋ฉด 'ํ๋ก์ ํธ ์์' ๋ฉ์์ง ์จ๊ธฐ๊ธฐ
|
1109 |
-
$id('noProjects').style.display = 'none';
|
1110 |
-
}
|
1111 |
|
1112 |
-
/* โโ ์ ํธ โโ */
|
1113 |
-
function $id(id){return document.getElementById(id)}
|
1114 |
-
|
1115 |
-
/* โโ ํ๋ก์ ํธ ์ ์ฅ โโ */
|
1116 |
function save(pages, title, isCached = false){
|
1117 |
const id=projects.push(pages)-1;
|
1118 |
addCard(id, pages[0].thumb, title, isCached);
|
@@ -1121,7 +1042,6 @@ HTML = """
|
|
1121 |
/* โโ ์๋ฒ PDF ๋ก๋ ๋ฐ ์บ์ ์ํ ํ์ธ โโ */
|
1122 |
async function loadServerPDFs() {
|
1123 |
try {
|
1124 |
-
// ๋ก๋ฉ ํ์ ์ถ๊ฐ
|
1125 |
if (document.querySelectorAll('.card').length === 0) {
|
1126 |
showLoading("๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ก๋ฉ ์ค...");
|
1127 |
}
|
@@ -1140,16 +1060,15 @@ HTML = """
|
|
1140 |
return;
|
1141 |
}
|
1142 |
|
1143 |
-
// ์๋ฒ PDF ๋ก๋ ๋ฐ ์ธ๋ค์ผ ์์ฑ (๋ณ๋ ฌ ์ฒ๋ฆฌ๋ก ์ต์ ํ)
|
1144 |
const thumbnailPromises = serverProjects.map(async (project, index) => {
|
1145 |
-
updateLoading(
|
1146 |
|
1147 |
const pdfName = project.name;
|
1148 |
const isCached = cacheStatus[pdfName] && cacheStatus[pdfName].status === "completed";
|
1149 |
|
1150 |
try {
|
1151 |
// ์ธ๋ค์ผ ๊ฐ์ ธ์ค๊ธฐ
|
1152 |
-
const response = await fetch(
|
1153 |
const data = await response.json();
|
1154 |
|
1155 |
if(data.thumbnail) {
|
@@ -1159,31 +1078,23 @@ HTML = """
|
|
1159 |
path: project.path,
|
1160 |
cached: isCached
|
1161 |
}];
|
1162 |
-
|
1163 |
-
return {
|
1164 |
-
pages,
|
1165 |
-
name: project.name,
|
1166 |
-
isCached
|
1167 |
-
};
|
1168 |
}
|
1169 |
} catch (err) {
|
1170 |
-
console.error(
|
1171 |
}
|
1172 |
|
1173 |
return null;
|
1174 |
});
|
1175 |
|
1176 |
-
// ๋ชจ๋ ์ธ๋ค์ผ ์์ฒญ ๊ธฐ๋ค๋ฆฌ๊ธฐ
|
1177 |
const results = await Promise.all(thumbnailPromises);
|
1178 |
|
1179 |
-
// ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์จ ๊ฒฐ๊ณผ๋ง ํ์
|
1180 |
results.filter(result => result !== null).forEach(result => {
|
1181 |
save(result.pages, result.name, result.isCached);
|
1182 |
});
|
1183 |
|
1184 |
hideLoading();
|
1185 |
|
1186 |
-
// ํ๋ก์ ํธ๊ฐ ์์ ๊ฒฝ์ฐ ๋ฉ์์ง ํ์
|
1187 |
if (document.querySelectorAll('.card').length === 0) {
|
1188 |
$id('noProjects').style.display = 'block';
|
1189 |
}
|
@@ -1208,7 +1119,6 @@ HTML = """
|
|
1208 |
const pdfPath = projects[i][0].path;
|
1209 |
const pdfName = pdfPath.split('/').pop().replace('.pdf', '');
|
1210 |
|
1211 |
-
// ์บ์ ์ํ ๋ฑ์ง ์
๋ฐ์ดํธ
|
1212 |
let badgeEl = cards[i].querySelector('.cached-status');
|
1213 |
|
1214 |
if(cacheStatus[pdfName] && cacheStatus[pdfName].status === "completed") {
|
@@ -1228,7 +1138,7 @@ HTML = """
|
|
1228 |
badgeEl.className = 'cached-status';
|
1229 |
cards[i].querySelector('.card-inner')?.appendChild(badgeEl);
|
1230 |
}
|
1231 |
-
badgeEl.textContent =
|
1232 |
badgeEl.style.background = 'var(--secondary-color)';
|
1233 |
}
|
1234 |
}
|
@@ -1237,27 +1147,21 @@ HTML = """
|
|
1237 |
// ํ์ฌ ๋ก๋ฉ ์ค์ธ PDF๊ฐ ์์ผ๋ฉด ์ํ ํ์ธ
|
1238 |
if (currentLoadingPdfPath && pageLoadingInterval) {
|
1239 |
const pdfName = currentLoadingPdfPath.split('/').pop().replace('.pdf', '');
|
1240 |
-
|
1241 |
if (cacheStatus[pdfName]) {
|
1242 |
const status = cacheStatus[pdfName].status;
|
1243 |
const progress = cacheStatus[pdfName].progress || 0;
|
1244 |
|
1245 |
if (status === "completed") {
|
1246 |
-
// ์บ์ฑ ์๋ฃ ์
|
1247 |
clearInterval(pageLoadingInterval);
|
1248 |
$id('loadingPages').style.display = 'none';
|
1249 |
currentLoadingPdfPath = null;
|
1250 |
-
|
1251 |
-
// ์๋ฃ๋ ์บ์๋ก ํ๋ฆฝ๋ถ ๋ค์ ๋ก๋
|
1252 |
refreshFlipBook();
|
1253 |
} else if (status === "processing") {
|
1254 |
-
// ์งํ ์ค์ผ ๋ ํ์ ์
๋ฐ์ดํธ
|
1255 |
$id('loadingPages').style.display = 'block';
|
1256 |
-
$id('loadingPagesCount').textContent =
|
1257 |
}
|
1258 |
}
|
1259 |
}
|
1260 |
-
|
1261 |
} catch(error) {
|
1262 |
console.error('์บ์ ์ํ ํ์ธ ์ค๋ฅ:', error);
|
1263 |
}
|
@@ -1268,27 +1172,20 @@ HTML = """
|
|
1268 |
toggle(false);
|
1269 |
const pages = projects[i];
|
1270 |
|
1271 |
-
// ๊ธฐ์กด FlipBook ์ ๋ฆฌ
|
1272 |
if(fb) {
|
1273 |
fb.destroy();
|
1274 |
viewer.innerHTML = '';
|
1275 |
}
|
1276 |
|
1277 |
-
// ์๋ฒ PDF ๋๋ ๋ก์ปฌ ํ๋ก์ ํธ ์ฒ๋ฆฌ
|
1278 |
if(pages[0].path) {
|
1279 |
const pdfPath = pages[0].path;
|
1280 |
-
|
1281 |
-
// ์ ์ง์ ๋ก๋ฉ ํ๋๊ทธ ์ด๊ธฐํ
|
1282 |
let progressiveLoading = false;
|
1283 |
currentLoadingPdfPath = pdfPath;
|
1284 |
|
1285 |
-
// ์บ์ ์ฌ๋ถ ํ์ธ
|
1286 |
if(pages[0].cached) {
|
1287 |
-
// ์บ์๋ PDF ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
|
1288 |
showLoading("์บ์๋ PDF ๋ก๋ฉ ์ค...");
|
1289 |
-
|
1290 |
try {
|
1291 |
-
const response = await fetch(
|
1292 |
const cachedData = await response.json();
|
1293 |
|
1294 |
if(cachedData.status === "completed" && cachedData.pages) {
|
@@ -1297,29 +1194,22 @@ HTML = """
|
|
1297 |
currentLoadingPdfPath = null;
|
1298 |
return;
|
1299 |
} else if(cachedData.status === "processing" && cachedData.pages && cachedData.pages.length > 0) {
|
1300 |
-
// ์ผ๋ถ ํ์ด์ง๊ฐ ์ด๋ฏธ ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ ์ ์ง์ ๋ก๋ฉ ์ฌ์ฉ
|
1301 |
hideLoading();
|
1302 |
createFlipBook(cachedData.pages);
|
1303 |
progressiveLoading = true;
|
1304 |
-
|
1305 |
-
// ์ ์ง์ ๋ก๋ฉ ์ค์์ ํ์
|
1306 |
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
1307 |
}
|
1308 |
} catch(error) {
|
1309 |
console.error("์บ์ ๋ฐ์ดํฐ ๋ก๋ ์ค๋ฅ:", error);
|
1310 |
-
// ์บ์ ๋ก๋ฉ ์คํจ ์ ์๋ณธ PDF๋ก ๋์ฒด
|
1311 |
}
|
1312 |
}
|
1313 |
|
1314 |
-
if
|
1315 |
-
// ์บ์๊ฐ ์๊ฑฐ๋ ๋ก๋ฉ ์คํจ ์ ์๋ฒ PDF ๋ก๋
|
1316 |
showLoading("PDF ์ค๋น ์ค...");
|
1317 |
-
|
1318 |
try {
|
1319 |
-
const response = await fetch(
|
1320 |
const data = await response.json();
|
1321 |
|
1322 |
-
// ์บ์๋ก ๋ฆฌ๋ค์ด๋ ํธ๋ ๊ฒฝ์ฐ
|
1323 |
if(data.redirect) {
|
1324 |
const redirectRes = await fetch(data.redirect);
|
1325 |
const cachedData = await redirectRes.json();
|
@@ -1330,20 +1220,15 @@ HTML = """
|
|
1330 |
currentLoadingPdfPath = null;
|
1331 |
return;
|
1332 |
} else if(cachedData.status === "processing" && cachedData.pages && cachedData.pages.length > 0) {
|
1333 |
-
// ์ผ๋ถ ํ์ด์ง๊ฐ ์ด๋ฏธ ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ ์ ์ง์ ๋ก๋ฉ ์ฌ์ฉ
|
1334 |
hideLoading();
|
1335 |
createFlipBook(cachedData.pages);
|
1336 |
-
|
1337 |
-
// ์ ์ง์ ๋ก๋ฉ ์ค์์ ํ์
|
1338 |
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
1339 |
return;
|
1340 |
}
|
1341 |
}
|
1342 |
|
1343 |
-
|
1344 |
-
const pdfResponse = await fetch(`/api/pdf-content?path=${encodeURIComponent(pdfPath)}`);
|
1345 |
|
1346 |
-
// JSON ์๋ต์ธ ๊ฒฝ์ฐ (๋ฆฌ๋ค์ด๋ ํธ ๋ฑ)
|
1347 |
try {
|
1348 |
const jsonData = await pdfResponse.clone().json();
|
1349 |
if (jsonData.redirect) {
|
@@ -1353,7 +1238,6 @@ HTML = """
|
|
1353 |
if(cachedData.pages && cachedData.pages.length > 0) {
|
1354 |
hideLoading();
|
1355 |
createFlipBook(cachedData.pages);
|
1356 |
-
|
1357 |
if(cachedData.status === "processing") {
|
1358 |
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
1359 |
} else {
|
@@ -1363,78 +1247,63 @@ HTML = """
|
|
1363 |
}
|
1364 |
}
|
1365 |
} catch (e) {
|
1366 |
-
// JSON ํ์ฑ ์คํจ
|
1367 |
}
|
1368 |
|
1369 |
-
// ArrayBuffer ํํ์ PDF ๋ฐ์ดํฐ
|
1370 |
const pdfData = await pdfResponse.arrayBuffer();
|
1371 |
-
|
1372 |
-
// PDF ๋ก๋ ๋ฐ ํ์ด์ง ๋ ๋๋ง
|
1373 |
const pdf = await pdfjsLib.getDocument({data: pdfData}).promise;
|
1374 |
const pdfPages = [];
|
1375 |
|
1376 |
-
for(let p
|
1377 |
-
updateLoading(
|
1378 |
-
|
1379 |
const pg = await pdf.getPage(p);
|
1380 |
const vp = pg.getViewport({scale: 1});
|
1381 |
const c = document.createElement('canvas');
|
1382 |
c.width = vp.width;
|
1383 |
c.height = vp.height;
|
1384 |
-
|
1385 |
await pg.render({canvasContext: c.getContext('2d'), viewport: vp}).promise;
|
1386 |
pdfPages.push({src: c.toDataURL(), thumb: c.toDataURL()});
|
1387 |
}
|
1388 |
-
|
1389 |
hideLoading();
|
1390 |
createFlipBook(pdfPages);
|
1391 |
currentLoadingPdfPath = null;
|
1392 |
-
|
1393 |
} catch(error) {
|
1394 |
-
console.error('PDF ์ฒ๋ฆฌ ์ค
|
1395 |
hideLoading();
|
1396 |
showError("PDF๋ฅผ ๋ก๋ํ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
1397 |
currentLoadingPdfPath = null;
|
1398 |
}
|
1399 |
}
|
1400 |
} else {
|
1401 |
-
// ๋ก์ปฌ ์
๋ก๋๋ ํ๋ก์ ํธ ์คํ
|
1402 |
createFlipBook(pages);
|
1403 |
currentLoadingPdfPath = null;
|
1404 |
}
|
1405 |
}
|
1406 |
|
1407 |
-
/* โโ ์ ์ง์ ๋ก๋ฉ ์ธ๋์ผ์ดํฐ ์์ โโ */
|
1408 |
function startProgressiveLoadingIndicator(progress, totalPages) {
|
1409 |
-
// ์งํ ์ํ ํ์ ํ์ฑํ
|
1410 |
$id('loadingPages').style.display = 'block';
|
1411 |
-
$id('loadingPagesCount').textContent =
|
1412 |
|
1413 |
-
// ๊ธฐ์กด ์ธํฐ๋ฒ ์ ๊ฑฐ
|
1414 |
if (pageLoadingInterval) {
|
1415 |
clearInterval(pageLoadingInterval);
|
1416 |
}
|
1417 |
-
|
1418 |
-
// ์ฃผ๊ธฐ์ ์ผ๋ก ์บ์ ์ํ ํ์ธ (2์ด๋ง๋ค)
|
1419 |
pageLoadingInterval = setInterval(async () => {
|
1420 |
if (!currentLoadingPdfPath) {
|
1421 |
clearInterval(pageLoadingInterval);
|
1422 |
$id('loadingPages').style.display = 'none';
|
1423 |
return;
|
1424 |
}
|
1425 |
-
|
1426 |
try {
|
1427 |
-
const response = await fetch(
|
1428 |
const status = await response.json();
|
1429 |
|
1430 |
-
// ์ํ ์
๋ฐ์ดํธ
|
1431 |
if (status.status === "completed") {
|
1432 |
clearInterval(pageLoadingInterval);
|
1433 |
$id('loadingPages').style.display = 'none';
|
1434 |
-
refreshFlipBook();
|
1435 |
currentLoadingPdfPath = null;
|
1436 |
} else if (status.status === "processing") {
|
1437 |
-
$id('loadingPagesCount').textContent =
|
1438 |
}
|
1439 |
} catch (e) {
|
1440 |
console.error("์บ์ ์ํ ํ์ธ ์ค๋ฅ:", e);
|
@@ -1442,20 +1311,15 @@ HTML = """
|
|
1442 |
}, 1000);
|
1443 |
}
|
1444 |
|
1445 |
-
/* โโ ํ๋ฆฝ๋ถ ์๋ก๊ณ ์นจ โโ */
|
1446 |
async function refreshFlipBook() {
|
1447 |
if (!currentLoadingPdfPath || !fb) return;
|
1448 |
-
|
1449 |
try {
|
1450 |
-
const response = await fetch(
|
1451 |
const cachedData = await response.json();
|
1452 |
|
1453 |
if(cachedData.status === "completed" && cachedData.pages) {
|
1454 |
-
// ๊ธฐ์กด ํ๋ฆฝ๋ถ ์ ๋ฆฌ
|
1455 |
fb.destroy();
|
1456 |
viewer.innerHTML = '';
|
1457 |
-
|
1458 |
-
// ์ ๋ฐ์ดํฐ๋ก ์ฌ์์ฑ
|
1459 |
createFlipBook(cachedData.pages);
|
1460 |
currentLoadingPdfPath = null;
|
1461 |
}
|
@@ -1466,47 +1330,39 @@ HTML = """
|
|
1466 |
|
1467 |
function createFlipBook(pages) {
|
1468 |
console.log('FlipBook ์์ฑ ์์. ํ์ด์ง ์:', pages.length);
|
1469 |
-
|
1470 |
try {
|
1471 |
-
// ํ๋ฉด ๋น์จ ๊ณ์ฐ
|
1472 |
const calculateAspectRatio = () => {
|
1473 |
const windowWidth = window.innerWidth;
|
1474 |
const windowHeight = window.innerHeight;
|
1475 |
const aspectRatio = windowWidth / windowHeight;
|
1476 |
|
1477 |
-
// ๋๋น ๋๋ ๋์ด ๊ธฐ์ค์ผ๋ก ์ต๋ 90% ์ ํ
|
1478 |
let width, height;
|
1479 |
-
if (aspectRatio > 1) {
|
1480 |
height = Math.min(windowHeight * 0.9, windowHeight - 40);
|
1481 |
-
width = height * aspectRatio * 0.8;
|
1482 |
if (width > windowWidth * 0.9) {
|
1483 |
width = windowWidth * 0.9;
|
1484 |
height = width / (aspectRatio * 0.8);
|
1485 |
}
|
1486 |
-
} else {
|
1487 |
width = Math.min(windowWidth * 0.9, windowWidth - 40);
|
1488 |
-
height = width / aspectRatio * 0.9;
|
1489 |
if (height > windowHeight * 0.9) {
|
1490 |
height = windowHeight * 0.9;
|
1491 |
width = height * aspectRatio * 0.9;
|
1492 |
}
|
1493 |
}
|
1494 |
-
|
1495 |
-
// ์ต์ ์ฌ์ด์ฆ ๋ฐํ
|
1496 |
return {
|
1497 |
width: Math.round(width),
|
1498 |
height: Math.round(height)
|
1499 |
};
|
1500 |
};
|
1501 |
|
1502 |
-
// ์ด๊ธฐ ํ๋ฉด ๋น์จ ๊ณ์ฐ
|
1503 |
const size = calculateAspectRatio();
|
1504 |
viewer.style.width = size.width + 'px';
|
1505 |
viewer.style.height = size.height + 'px';
|
1506 |
|
1507 |
-
// ํ์ด์ง ๋ฐ์ดํฐ ์ ์ (๋น ํ์ด์ง ์ฒ๋ฆฌ)
|
1508 |
const validPages = pages.map(page => {
|
1509 |
-
// src๊ฐ ์๋ ํ์ด์ง๋ ๋ก๋ฉ ์ค ์ด๋ฏธ์ง๋ก ๋์ฒด
|
1510 |
if (!page || !page.src) {
|
1511 |
return {
|
1512 |
src: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZjVmNWY1Ii8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iIGZpbGw9IiM1NTUiPkxvYWRpbmcuLi48L3RleHQ+PC9zdmc+',
|
@@ -1522,7 +1378,6 @@ HTML = """
|
|
1522 |
autoSize: true,
|
1523 |
flipDuration: 800,
|
1524 |
backgroundColor: '#fff',
|
1525 |
-
/* ๐ ๋ด์ฅ ์ฌ์ด๋ */
|
1526 |
sound: true,
|
1527 |
assets: {flipMp3: 'static/turnPage2.mp3', hardFlipMp3: 'static/turnPage2.mp3'},
|
1528 |
controlsProps: {
|
@@ -1537,22 +1392,21 @@ HTML = """
|
|
1537 |
enableAnnotation: false,
|
1538 |
enableSound: true,
|
1539 |
enableLightbox: false,
|
1540 |
-
layout: 10,
|
1541 |
-
skin: 'light',
|
1542 |
-
autoNavigationTime: 3600,
|
1543 |
-
hideControls: false,
|
1544 |
-
paddingTop: 10,
|
1545 |
-
paddingLeft: 10,
|
1546 |
-
paddingRight: 10,
|
1547 |
-
paddingBottom: 10,
|
1548 |
-
pageTextureSize: 1024,
|
1549 |
-
thumbnails: true,
|
1550 |
-
autoHideControls: false,
|
1551 |
-
controlsTimeout: 8000
|
1552 |
}
|
1553 |
});
|
1554 |
|
1555 |
-
// ํ๋ฉด ํฌ๊ธฐ ๋ณ๊ฒฝ ์ FlipBook ํฌ๊ธฐ ์กฐ์
|
1556 |
window.addEventListener('resize', () => {
|
1557 |
if (fb) {
|
1558 |
const newSize = calculateAspectRatio();
|
@@ -1562,10 +1416,8 @@ HTML = """
|
|
1562 |
}
|
1563 |
});
|
1564 |
|
1565 |
-
// FlipBook ์์ฑ ํ ์ปจํธ๋กค๋ฐ ๊ฐ์ ํ์
|
1566 |
setTimeout(() => {
|
1567 |
try {
|
1568 |
-
// ์ปจํธ๋กค๋ฐ ๊ด๋ จ ์์ ์ฐพ๊ธฐ ๋ฐ ์คํ์ผ ์ ์ฉ
|
1569 |
const menuBars = document.querySelectorAll('.flipbook-container .fb3d-menu-bar');
|
1570 |
if (menuBars && menuBars.length > 0) {
|
1571 |
menuBars.forEach(menuBar => {
|
@@ -1587,15 +1439,26 @@ HTML = """
|
|
1587 |
}
|
1588 |
}
|
1589 |
|
1590 |
-
|
1591 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1592 |
|
1593 |
function toggle(showHome){
|
1594 |
-
$id('home').style.display=showHome?'block':'none';
|
1595 |
-
$id('viewerPage').style.display=showHome?'none':'block';
|
1596 |
-
$id('homeButton').style.display=showHome?'none':'block';
|
1597 |
|
1598 |
-
// ๋ทฐ์ด ๋ชจ๋์ผ ๋ ์คํ์ผ ๋ณ๊ฒฝ
|
1599 |
if(!showHome) {
|
1600 |
document.body.classList.add('viewer-mode');
|
1601 |
} else {
|
@@ -1603,11 +1466,8 @@ HTML = """
|
|
1603 |
}
|
1604 |
}
|
1605 |
|
1606 |
-
/* -- ๋ก๋ฉ ๋ฐ ์ค๋ฅ ํ์ -- */
|
1607 |
function showLoading(message, progress = -1) {
|
1608 |
-
// ๊ธฐ์กด ๋ก๋ฉ ์ปจํ
์ด๋๊ฐ ์๋ค๋ฉด ์ ๊ฑฐ
|
1609 |
hideLoading();
|
1610 |
-
|
1611 |
const loadingContainer = document.createElement('div');
|
1612 |
loadingContainer.className = 'loading-container fade-in';
|
1613 |
loadingContainer.id = 'loadingContainer';
|
@@ -1638,7 +1498,6 @@ HTML = """
|
|
1638 |
|
1639 |
if (progress >= 0) {
|
1640 |
let progressBar = $id('progressBar');
|
1641 |
-
|
1642 |
if (!progressBar) {
|
1643 |
const loadingContainer = $id('loadingContainer');
|
1644 |
if (loadingContainer) {
|
@@ -1649,7 +1508,7 @@ HTML = """
|
|
1649 |
progressBar = $id('progressBar');
|
1650 |
}
|
1651 |
} else {
|
1652 |
-
progressBar.style.width =
|
1653 |
}
|
1654 |
}
|
1655 |
}
|
@@ -1662,7 +1521,6 @@ HTML = """
|
|
1662 |
}
|
1663 |
|
1664 |
function showError(message) {
|
1665 |
-
// ๊ธฐ์กด ์ค๋ฅ ๋ฉ์์ง๊ฐ ์๋ค๋ฉด ์ ๊ฑฐ
|
1666 |
const existingError = $id('errorContainer');
|
1667 |
if (existingError) {
|
1668 |
existingError.remove();
|
@@ -1678,12 +1536,10 @@ HTML = """
|
|
1678 |
|
1679 |
document.body.appendChild(errorContainer);
|
1680 |
|
1681 |
-
// ํ์ธ ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ
|
1682 |
$id('errorCloseBtn').onclick = () => {
|
1683 |
errorContainer.remove();
|
1684 |
};
|
1685 |
|
1686 |
-
// 5์ด ํ ์๋์ผ๋ก ๋ซ๊ธฐ
|
1687 |
setTimeout(() => {
|
1688 |
if ($id('errorContainer')) {
|
1689 |
$id('errorContainer').remove();
|
@@ -1691,7 +1547,15 @@ HTML = """
|
|
1691 |
}, 5000);
|
1692 |
}
|
1693 |
|
1694 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1695 |
</script>
|
1696 |
</body>
|
1697 |
</html>
|
@@ -1702,4 +1566,4 @@ async def root():
|
|
1702 |
return get_html_content()
|
1703 |
|
1704 |
if __name__ == "__main__":
|
1705 |
-
uvicorn.run("app:app", host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
|
|
|
206 |
except Exception as e:
|
207 |
import traceback
|
208 |
logger.error(f"PDF ์บ์ฑ ์ค๋ฅ: {str(e)}\n{traceback.format_exc()}")
|
209 |
+
if 'pdf_name' in locals() and pdf_name in pdf_cache:
|
210 |
pdf_cache[pdf_name]["status"] = "error"
|
211 |
pdf_cache[pdf_name]["error"] = str(e)
|
212 |
|
|
|
310 |
pages = pdf_cache[pdf_name].get("pages", [])
|
311 |
total_pages = pdf_cache[pdf_name].get("total_pages", 0)
|
312 |
|
|
|
313 |
return {
|
314 |
"status": "processing",
|
315 |
"progress": progress,
|
|
|
416 |
|
417 |
body {
|
418 |
margin: 0;
|
419 |
+
/* โผ ๊ณ ๊ธ์ง ๋ธ๋ฃจ ๊ณ์ด ๊ทธ๋ผ๋์์ด์
์ผ๋ก ๋ณ๊ฒฝ */
|
420 |
+
background-image: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%);
|
421 |
+
background-attachment: fixed;
|
422 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
423 |
color: var(--text-color);
|
|
|
|
|
424 |
}
|
425 |
|
426 |
/* ํค๋ ์ ๋ชฉ ์ ๊ฑฐ ๋ฐ Home ๋ฒํผ ๋ ์ด์ด ์ฒ๋ฆฌ */
|
|
|
483 |
opacity: 1;
|
484 |
transform: translateX(0);
|
485 |
}
|
486 |
+
|
487 |
#home, #viewerPage {
|
488 |
padding-top: 100px;
|
489 |
max-width: 1200px;
|
|
|
509 |
background: white;
|
510 |
margin: 0 10px;
|
511 |
font-weight: 500;
|
512 |
+
display: inline-flex;
|
513 |
align-items: center;
|
514 |
box-shadow: var(--shadow-sm);
|
515 |
transition: var(--transition);
|
|
|
524 |
left: 0;
|
525 |
width: 100%;
|
526 |
height: 100%;
|
527 |
+
background: linear-gradient(120deg, var(--primary-color), var(--secondary-color));
|
528 |
opacity: 0.08;
|
529 |
z-index: -1;
|
530 |
}
|
|
|
800 |
}
|
801 |
|
802 |
/* ํค๋ ๋ก๊ณ ๋ฐ ํ์ดํ */
|
803 |
+
/* โผ pointer-events: none ์ ๊ฑฐ, ๋์ด์ ํจ๋ฉ ์กฐ์ ํ์ฌ ์ ๋ชฉ์ ๋ ์๋ก */
|
804 |
.library-header {
|
805 |
position: fixed;
|
806 |
+
top: 10px; /* ๊ธฐ์กด 20px -> 10px์ผ๋ก */
|
807 |
left: 0;
|
808 |
right: 0;
|
809 |
text-align: center;
|
810 |
z-index: 100;
|
|
|
811 |
}
|
812 |
|
813 |
.library-header .title {
|
814 |
display: inline-block;
|
815 |
+
/* ๋์ด ์ค์ด๊ธฐ ์ํ ํจ๋ฉ/ํฐํธ ํฌ๊ธฐ ์กฐ์ */
|
816 |
+
padding: 8px 20px; /* ๊ธฐ์กด 12px 30px -> 8px 20px */
|
817 |
background: rgba(255, 255, 255, 0.85);
|
818 |
backdrop-filter: blur(10px);
|
819 |
border-radius: 30px;
|
820 |
box-shadow: var(--shadow-md);
|
821 |
+
font-size: 1.2rem; /* ๊ธฐ์กด 1.5rem -> 1.2rem */
|
822 |
font-weight: 600;
|
823 |
background-image: linear-gradient(120deg, #667eea 0%, #764ba2 100%);
|
824 |
-webkit-background-clip: text;
|
825 |
background-clip: text;
|
826 |
color: transparent;
|
|
|
827 |
}
|
828 |
|
829 |
/* ์ ์ง์ ๋ก๋ฉ ํ์ */
|
|
|
854 |
}
|
855 |
|
856 |
.library-header .title {
|
857 |
+
font-size: 1rem;
|
858 |
+
padding: 6px 16px;
|
859 |
}
|
860 |
|
861 |
.floating-home {
|
|
|
883 |
|
884 |
<section id="home" class="fade-in">
|
885 |
<div class="upload-container">
|
886 |
+
<button class="upload" id="imageUploadBtn">
|
887 |
<i class="fas fa-images"></i> ์ด๋ฏธ์ง ์ถ๊ฐ
|
888 |
+
<input id="imgInput" type="file" accept="image/*" multiple hidden>
|
889 |
</button>
|
890 |
+
<button class="upload" id="pdfUploadBtn">
|
891 |
<i class="fas fa-file-pdf"></i> PDF ์ถ๊ฐ
|
892 |
+
<input id="pdfInput" type="file" accept="application/pdf" hidden>
|
893 |
</button>
|
894 |
</div>
|
895 |
|
|
|
|
|
|
|
|
|
896 |
<div class="section-title">๋ด ํ๋ก์ ํธ</div>
|
897 |
<div class="grid" id="grid">
|
898 |
<!-- ์นด๋๊ฐ ์ฌ๊ธฐ์ ๋์ ์ผ๋ก ์ถ๊ฐ๋ฉ๋๋ค -->
|
|
|
925 |
.play().then(a=>a.pause()).catch(()=>{});document.removeEventListener(evt,u,{capture:true});},
|
926 |
{once:true,capture:true});
|
927 |
});
|
928 |
+
|
929 |
+
/* โโ ์ ํธ โโ */
|
930 |
+
function $id(id){return document.getElementById(id)}
|
931 |
+
|
932 |
+
// ์ง์ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ค์ (์
๋ก๋ ๋ฒํผ ํด๋ฆญ์ ํ์ผ ์ ํ์ฐฝ ์ด๊ธฐ)
|
933 |
+
function setupDirectEvents() {
|
934 |
+
// ์ด๋ฏธ์ง ์
๋ก๋ ๋ฒํผ
|
935 |
+
const imageBtn = $id('imageUploadBtn');
|
936 |
+
const imageInput = $id('imgInput');
|
937 |
+
if (imageBtn && imageInput) {
|
938 |
+
console.log('์ด๋ฏธ์ง ์
๋ก๋ ๋ฒํผ ์ด๋ฒคํธ ์ค์ ');
|
939 |
+
imageBtn.onclick = function(e) {
|
940 |
+
e.preventDefault();
|
941 |
+
e.stopPropagation();
|
942 |
+
imageInput.click();
|
943 |
+
};
|
|
|
944 |
}
|
945 |
|
946 |
+
// PDF ์
๋ก๋ ๋ฒํผ
|
947 |
+
const pdfBtn = $id('pdfUploadBtn');
|
948 |
+
const pdfInput = $id('pdfInput');
|
949 |
+
if (pdfBtn && pdfInput) {
|
950 |
+
console.log('PDF ์
๋ก๋ ๋ฒํผ ์ด๋ฒคํธ ์ค์ ');
|
951 |
+
pdfBtn.onclick = function(e) {
|
952 |
+
e.preventDefault();
|
953 |
+
e.stopPropagation();
|
954 |
pdfInput.click();
|
955 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
956 |
}
|
957 |
+
}
|
958 |
+
|
959 |
+
function addCard(i, thumb, title, isCached = false) {
|
960 |
+
const d = document.createElement('div');
|
961 |
+
d.className = 'card fade-in';
|
962 |
+
d.onclick = () => open(i);
|
963 |
|
964 |
+
const displayTitle = title ?
|
965 |
+
(title.length > 15 ? title.substring(0, 15) + '...' : title) :
|
966 |
+
'ํ๋ก์ ํธ ' + (i+1);
|
|
|
|
|
|
|
967 |
|
968 |
+
const cachedBadge = isCached ?
|
969 |
+
'<div class="cached-status">์บ์๋จ</div>' : '';
|
|
|
|
|
|
|
|
|
|
|
|
|
970 |
|
971 |
+
d.innerHTML = `
|
972 |
+
<div class="card-inner">
|
973 |
+
${cachedBadge}
|
974 |
+
<img src="${thumb}" alt="${displayTitle}" loading="lazy">
|
975 |
+
<p title="${title || 'ํ๋ก์ ํธ ' + (i+1)}">${displayTitle}</p>
|
976 |
+
</div>
|
977 |
+
`;
|
978 |
+
grid.appendChild(d);
|
979 |
|
980 |
+
// ํ๋ก์ ํธ๊ฐ ์์ผ๋ฉด 'ํ๋ก์ ํธ ์์' ๋ฉ์์ง ์จ๊ธฐ๊ธฐ
|
981 |
+
$id('noProjects').style.display = 'none';
|
982 |
+
}
|
983 |
+
|
984 |
+
/* โโ ์ด๋ฏธ์ง ์
๋ก๋ โโ */
|
985 |
+
$id('imgInput').onchange = e => {
|
986 |
+
const files = [...e.target.files];
|
987 |
+
if(!files.length) return;
|
988 |
|
|
|
989 |
showLoading("์ด๋ฏธ์ง ๋ก๋ฉ ์ค...");
|
990 |
|
991 |
+
const pages=[], tot = files.length;
|
992 |
let done = 0;
|
993 |
+
files.forEach((f,i)=> {
|
994 |
+
const r=new FileReader();
|
995 |
+
r.onload=x=>{
|
996 |
+
pages[i] = {src: x.target.result, thumb: x.target.result};
|
997 |
+
if(++done===tot) {
|
|
|
|
|
|
|
|
|
|
|
|
|
998 |
save(pages, '์ด๋ฏธ์ง ์ปฌ๋ ์
');
|
999 |
hideLoading();
|
1000 |
}
|
1001 |
};
|
1002 |
+
r.readAsDataURL(f);
|
1003 |
});
|
1004 |
+
};
|
1005 |
+
|
1006 |
+
/* โโ PDF ์
๋ก๋ โโ */
|
1007 |
+
$id('pdfInput').onchange = e => {
|
1008 |
const file = e.target.files[0];
|
1009 |
+
if(!file) return;
|
1010 |
|
|
|
1011 |
showLoading("PDF ๋ก๋ฉ ์ค...");
|
1012 |
|
1013 |
+
const fr = new FileReader();
|
1014 |
+
fr.onload = v => {
|
1015 |
+
pdfjsLib.getDocument({data: v.target.result}).promise.then(async pdf=>{
|
1016 |
const pages = [];
|
1017 |
|
1018 |
+
for(let p=1; p <= pdf.numPages; p++){
|
1019 |
+
updateLoading(\`PDF ํ์ด์ง ๋ก๋ฉ ์ค... (\${p}/\${pdf.numPages})\`);
|
1020 |
+
const pg=await pdf.getPage(p), vp=pg.getViewport({scale:1});
|
1021 |
+
const c=document.createElement('canvas');
|
1022 |
+
c.width=vp.width;c.height=vp.height;
|
1023 |
+
await pg.render({canvasContext:c.getContext('2d'),viewport:vp}).promise;
|
1024 |
+
pages.push({src:c.toDataURL(), thumb:c.toDataURL()});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1025 |
}
|
|
|
1026 |
hideLoading();
|
1027 |
save(pages, file.name.replace('.pdf', ''));
|
1028 |
}).catch(error => {
|
|
|
1031 |
showError("PDF ๋ก๋ฉ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
1032 |
});
|
1033 |
};
|
1034 |
+
fr.readAsArrayBuffer(file);
|
1035 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1036 |
|
|
|
|
|
|
|
|
|
1037 |
function save(pages, title, isCached = false){
|
1038 |
const id=projects.push(pages)-1;
|
1039 |
addCard(id, pages[0].thumb, title, isCached);
|
|
|
1042 |
/* โโ ์๋ฒ PDF ๋ก๋ ๋ฐ ์บ์ ์ํ ํ์ธ โโ */
|
1043 |
async function loadServerPDFs() {
|
1044 |
try {
|
|
|
1045 |
if (document.querySelectorAll('.card').length === 0) {
|
1046 |
showLoading("๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ก๋ฉ ์ค...");
|
1047 |
}
|
|
|
1060 |
return;
|
1061 |
}
|
1062 |
|
|
|
1063 |
const thumbnailPromises = serverProjects.map(async (project, index) => {
|
1064 |
+
updateLoading(\`PDF ํ๋ก์ ํธ ๋ก๋ฉ ์ค... (\${index+1}/\${serverProjects.length})\`);
|
1065 |
|
1066 |
const pdfName = project.name;
|
1067 |
const isCached = cacheStatus[pdfName] && cacheStatus[pdfName].status === "completed";
|
1068 |
|
1069 |
try {
|
1070 |
// ์ธ๋ค์ผ ๊ฐ์ ธ์ค๊ธฐ
|
1071 |
+
const response = await fetch(\`/api/pdf-thumbnail?path=\${encodeURIComponent(project.path)}\`);
|
1072 |
const data = await response.json();
|
1073 |
|
1074 |
if(data.thumbnail) {
|
|
|
1078 |
path: project.path,
|
1079 |
cached: isCached
|
1080 |
}];
|
1081 |
+
return { pages, name: project.name, isCached };
|
|
|
|
|
|
|
|
|
|
|
1082 |
}
|
1083 |
} catch (err) {
|
1084 |
+
console.error(\`์ธ๋ค์ผ ๋ก๋ ์ค๋ฅ (\${project.name}):\`, err);
|
1085 |
}
|
1086 |
|
1087 |
return null;
|
1088 |
});
|
1089 |
|
|
|
1090 |
const results = await Promise.all(thumbnailPromises);
|
1091 |
|
|
|
1092 |
results.filter(result => result !== null).forEach(result => {
|
1093 |
save(result.pages, result.name, result.isCached);
|
1094 |
});
|
1095 |
|
1096 |
hideLoading();
|
1097 |
|
|
|
1098 |
if (document.querySelectorAll('.card').length === 0) {
|
1099 |
$id('noProjects').style.display = 'block';
|
1100 |
}
|
|
|
1119 |
const pdfPath = projects[i][0].path;
|
1120 |
const pdfName = pdfPath.split('/').pop().replace('.pdf', '');
|
1121 |
|
|
|
1122 |
let badgeEl = cards[i].querySelector('.cached-status');
|
1123 |
|
1124 |
if(cacheStatus[pdfName] && cacheStatus[pdfName].status === "completed") {
|
|
|
1138 |
badgeEl.className = 'cached-status';
|
1139 |
cards[i].querySelector('.card-inner')?.appendChild(badgeEl);
|
1140 |
}
|
1141 |
+
badgeEl.textContent = \`\${cacheStatus[pdfName].progress}%\`;
|
1142 |
badgeEl.style.background = 'var(--secondary-color)';
|
1143 |
}
|
1144 |
}
|
|
|
1147 |
// ํ์ฌ ๋ก๋ฉ ์ค์ธ PDF๊ฐ ์์ผ๋ฉด ์ํ ํ์ธ
|
1148 |
if (currentLoadingPdfPath && pageLoadingInterval) {
|
1149 |
const pdfName = currentLoadingPdfPath.split('/').pop().replace('.pdf', '');
|
|
|
1150 |
if (cacheStatus[pdfName]) {
|
1151 |
const status = cacheStatus[pdfName].status;
|
1152 |
const progress = cacheStatus[pdfName].progress || 0;
|
1153 |
|
1154 |
if (status === "completed") {
|
|
|
1155 |
clearInterval(pageLoadingInterval);
|
1156 |
$id('loadingPages').style.display = 'none';
|
1157 |
currentLoadingPdfPath = null;
|
|
|
|
|
1158 |
refreshFlipBook();
|
1159 |
} else if (status === "processing") {
|
|
|
1160 |
$id('loadingPages').style.display = 'block';
|
1161 |
+
$id('loadingPagesCount').textContent = \`\${progress}%\`;
|
1162 |
}
|
1163 |
}
|
1164 |
}
|
|
|
1165 |
} catch(error) {
|
1166 |
console.error('์บ์ ์ํ ํ์ธ ์ค๋ฅ:', error);
|
1167 |
}
|
|
|
1172 |
toggle(false);
|
1173 |
const pages = projects[i];
|
1174 |
|
|
|
1175 |
if(fb) {
|
1176 |
fb.destroy();
|
1177 |
viewer.innerHTML = '';
|
1178 |
}
|
1179 |
|
|
|
1180 |
if(pages[0].path) {
|
1181 |
const pdfPath = pages[0].path;
|
|
|
|
|
1182 |
let progressiveLoading = false;
|
1183 |
currentLoadingPdfPath = pdfPath;
|
1184 |
|
|
|
1185 |
if(pages[0].cached) {
|
|
|
1186 |
showLoading("์บ์๋ PDF ๋ก๋ฉ ์ค...");
|
|
|
1187 |
try {
|
1188 |
+
const response = await fetch(\`/api/cached-pdf?path=\${encodeURIComponent(pdfPath)}\`);
|
1189 |
const cachedData = await response.json();
|
1190 |
|
1191 |
if(cachedData.status === "completed" && cachedData.pages) {
|
|
|
1194 |
currentLoadingPdfPath = null;
|
1195 |
return;
|
1196 |
} else if(cachedData.status === "processing" && cachedData.pages && cachedData.pages.length > 0) {
|
|
|
1197 |
hideLoading();
|
1198 |
createFlipBook(cachedData.pages);
|
1199 |
progressiveLoading = true;
|
|
|
|
|
1200 |
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
1201 |
}
|
1202 |
} catch(error) {
|
1203 |
console.error("์บ์ ๋ฐ์ดํฐ ๋ก๋ ์ค๋ฅ:", error);
|
|
|
1204 |
}
|
1205 |
}
|
1206 |
|
1207 |
+
if(!progressiveLoading) {
|
|
|
1208 |
showLoading("PDF ์ค๋น ์ค...");
|
|
|
1209 |
try {
|
1210 |
+
const response = await fetch(\`/api/pdf-content?path=\${encodeURIComponent(pdfPath)}\`);
|
1211 |
const data = await response.json();
|
1212 |
|
|
|
1213 |
if(data.redirect) {
|
1214 |
const redirectRes = await fetch(data.redirect);
|
1215 |
const cachedData = await redirectRes.json();
|
|
|
1220 |
currentLoadingPdfPath = null;
|
1221 |
return;
|
1222 |
} else if(cachedData.status === "processing" && cachedData.pages && cachedData.pages.length > 0) {
|
|
|
1223 |
hideLoading();
|
1224 |
createFlipBook(cachedData.pages);
|
|
|
|
|
1225 |
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
1226 |
return;
|
1227 |
}
|
1228 |
}
|
1229 |
|
1230 |
+
const pdfResponse = await fetch(\`/api/pdf-content?path=\${encodeURIComponent(pdfPath)}\`);
|
|
|
1231 |
|
|
|
1232 |
try {
|
1233 |
const jsonData = await pdfResponse.clone().json();
|
1234 |
if (jsonData.redirect) {
|
|
|
1238 |
if(cachedData.pages && cachedData.pages.length > 0) {
|
1239 |
hideLoading();
|
1240 |
createFlipBook(cachedData.pages);
|
|
|
1241 |
if(cachedData.status === "processing") {
|
1242 |
startProgressiveLoadingIndicator(cachedData.progress, cachedData.total_pages);
|
1243 |
} else {
|
|
|
1247 |
}
|
1248 |
}
|
1249 |
} catch (e) {
|
1250 |
+
// JSON ํ์ฑ ์คํจ -> PDF ์๋ณธ ๋ฐ์ดํฐ๋ก ์ฒ๋ฆฌ
|
1251 |
}
|
1252 |
|
|
|
1253 |
const pdfData = await pdfResponse.arrayBuffer();
|
|
|
|
|
1254 |
const pdf = await pdfjsLib.getDocument({data: pdfData}).promise;
|
1255 |
const pdfPages = [];
|
1256 |
|
1257 |
+
for(let p=1; p <= pdf.numPages; p++) {
|
1258 |
+
updateLoading(\`ํ์ด์ง ์ค๋น ์ค... (\${p}/\${pdf.numPages})\`);
|
|
|
1259 |
const pg = await pdf.getPage(p);
|
1260 |
const vp = pg.getViewport({scale: 1});
|
1261 |
const c = document.createElement('canvas');
|
1262 |
c.width = vp.width;
|
1263 |
c.height = vp.height;
|
|
|
1264 |
await pg.render({canvasContext: c.getContext('2d'), viewport: vp}).promise;
|
1265 |
pdfPages.push({src: c.toDataURL(), thumb: c.toDataURL()});
|
1266 |
}
|
|
|
1267 |
hideLoading();
|
1268 |
createFlipBook(pdfPages);
|
1269 |
currentLoadingPdfPath = null;
|
|
|
1270 |
} catch(error) {
|
1271 |
+
console.error('PDF ์ฒ๋ฆฌ ์ค ์ค๋ฅ:', error);
|
1272 |
hideLoading();
|
1273 |
showError("PDF๋ฅผ ๋ก๋ํ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
|
1274 |
currentLoadingPdfPath = null;
|
1275 |
}
|
1276 |
}
|
1277 |
} else {
|
|
|
1278 |
createFlipBook(pages);
|
1279 |
currentLoadingPdfPath = null;
|
1280 |
}
|
1281 |
}
|
1282 |
|
|
|
1283 |
function startProgressiveLoadingIndicator(progress, totalPages) {
|
|
|
1284 |
$id('loadingPages').style.display = 'block';
|
1285 |
+
$id('loadingPagesCount').textContent = \`\${progress}%\`;
|
1286 |
|
|
|
1287 |
if (pageLoadingInterval) {
|
1288 |
clearInterval(pageLoadingInterval);
|
1289 |
}
|
|
|
|
|
1290 |
pageLoadingInterval = setInterval(async () => {
|
1291 |
if (!currentLoadingPdfPath) {
|
1292 |
clearInterval(pageLoadingInterval);
|
1293 |
$id('loadingPages').style.display = 'none';
|
1294 |
return;
|
1295 |
}
|
|
|
1296 |
try {
|
1297 |
+
const response = await fetch(\`/api/cache-status?path=\${encodeURIComponent(currentLoadingPdfPath)}\`);
|
1298 |
const status = await response.json();
|
1299 |
|
|
|
1300 |
if (status.status === "completed") {
|
1301 |
clearInterval(pageLoadingInterval);
|
1302 |
$id('loadingPages').style.display = 'none';
|
1303 |
+
refreshFlipBook();
|
1304 |
currentLoadingPdfPath = null;
|
1305 |
} else if (status.status === "processing") {
|
1306 |
+
$id('loadingPagesCount').textContent = \`\${status.progress}%\`;
|
1307 |
}
|
1308 |
} catch (e) {
|
1309 |
console.error("์บ์ ์ํ ํ์ธ ์ค๋ฅ:", e);
|
|
|
1311 |
}, 1000);
|
1312 |
}
|
1313 |
|
|
|
1314 |
async function refreshFlipBook() {
|
1315 |
if (!currentLoadingPdfPath || !fb) return;
|
|
|
1316 |
try {
|
1317 |
+
const response = await fetch(\`/api/cached-pdf?path=\${encodeURIComponent(currentLoadingPdfPath)}\`);
|
1318 |
const cachedData = await response.json();
|
1319 |
|
1320 |
if(cachedData.status === "completed" && cachedData.pages) {
|
|
|
1321 |
fb.destroy();
|
1322 |
viewer.innerHTML = '';
|
|
|
|
|
1323 |
createFlipBook(cachedData.pages);
|
1324 |
currentLoadingPdfPath = null;
|
1325 |
}
|
|
|
1330 |
|
1331 |
function createFlipBook(pages) {
|
1332 |
console.log('FlipBook ์์ฑ ์์. ํ์ด์ง ์:', pages.length);
|
|
|
1333 |
try {
|
|
|
1334 |
const calculateAspectRatio = () => {
|
1335 |
const windowWidth = window.innerWidth;
|
1336 |
const windowHeight = window.innerHeight;
|
1337 |
const aspectRatio = windowWidth / windowHeight;
|
1338 |
|
|
|
1339 |
let width, height;
|
1340 |
+
if (aspectRatio > 1) {
|
1341 |
height = Math.min(windowHeight * 0.9, windowHeight - 40);
|
1342 |
+
width = height * aspectRatio * 0.8;
|
1343 |
if (width > windowWidth * 0.9) {
|
1344 |
width = windowWidth * 0.9;
|
1345 |
height = width / (aspectRatio * 0.8);
|
1346 |
}
|
1347 |
+
} else {
|
1348 |
width = Math.min(windowWidth * 0.9, windowWidth - 40);
|
1349 |
+
height = width / aspectRatio * 0.9;
|
1350 |
if (height > windowHeight * 0.9) {
|
1351 |
height = windowHeight * 0.9;
|
1352 |
width = height * aspectRatio * 0.9;
|
1353 |
}
|
1354 |
}
|
|
|
|
|
1355 |
return {
|
1356 |
width: Math.round(width),
|
1357 |
height: Math.round(height)
|
1358 |
};
|
1359 |
};
|
1360 |
|
|
|
1361 |
const size = calculateAspectRatio();
|
1362 |
viewer.style.width = size.width + 'px';
|
1363 |
viewer.style.height = size.height + 'px';
|
1364 |
|
|
|
1365 |
const validPages = pages.map(page => {
|
|
|
1366 |
if (!page || !page.src) {
|
1367 |
return {
|
1368 |
src: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZjVmNWY1Ii8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iIGZpbGw9IiM1NTUiPkxvYWRpbmcuLi48L3RleHQ+PC9zdmc+',
|
|
|
1378 |
autoSize: true,
|
1379 |
flipDuration: 800,
|
1380 |
backgroundColor: '#fff',
|
|
|
1381 |
sound: true,
|
1382 |
assets: {flipMp3: 'static/turnPage2.mp3', hardFlipMp3: 'static/turnPage2.mp3'},
|
1383 |
controlsProps: {
|
|
|
1392 |
enableAnnotation: false,
|
1393 |
enableSound: true,
|
1394 |
enableLightbox: false,
|
1395 |
+
layout: 10,
|
1396 |
+
skin: 'light',
|
1397 |
+
autoNavigationTime: 3600,
|
1398 |
+
hideControls: false,
|
1399 |
+
paddingTop: 10,
|
1400 |
+
paddingLeft: 10,
|
1401 |
+
paddingRight: 10,
|
1402 |
+
paddingBottom: 10,
|
1403 |
+
pageTextureSize: 1024,
|
1404 |
+
thumbnails: true,
|
1405 |
+
autoHideControls: false,
|
1406 |
+
controlsTimeout: 8000
|
1407 |
}
|
1408 |
});
|
1409 |
|
|
|
1410 |
window.addEventListener('resize', () => {
|
1411 |
if (fb) {
|
1412 |
const newSize = calculateAspectRatio();
|
|
|
1416 |
}
|
1417 |
});
|
1418 |
|
|
|
1419 |
setTimeout(() => {
|
1420 |
try {
|
|
|
1421 |
const menuBars = document.querySelectorAll('.flipbook-container .fb3d-menu-bar');
|
1422 |
if (menuBars && menuBars.length > 0) {
|
1423 |
menuBars.forEach(menuBar => {
|
|
|
1439 |
}
|
1440 |
}
|
1441 |
|
1442 |
+
$id('homeButton').onclick = () => {
|
1443 |
+
if(fb) {
|
1444 |
+
fb.destroy();
|
1445 |
+
viewer.innerHTML = '';
|
1446 |
+
fb = null;
|
1447 |
+
}
|
1448 |
+
toggle(true);
|
1449 |
+
if (pageLoadingInterval) {
|
1450 |
+
clearInterval(pageLoadingInterval);
|
1451 |
+
pageLoadingInterval = null;
|
1452 |
+
}
|
1453 |
+
$id('loadingPages').style.display = 'none';
|
1454 |
+
currentLoadingPdfPath = null;
|
1455 |
+
};
|
1456 |
|
1457 |
function toggle(showHome){
|
1458 |
+
$id('home').style.display = showHome?'block':'none';
|
1459 |
+
$id('viewerPage').style.display = showHome?'none':'block';
|
1460 |
+
$id('homeButton').style.display = showHome?'none':'block';
|
1461 |
|
|
|
1462 |
if(!showHome) {
|
1463 |
document.body.classList.add('viewer-mode');
|
1464 |
} else {
|
|
|
1466 |
}
|
1467 |
}
|
1468 |
|
|
|
1469 |
function showLoading(message, progress = -1) {
|
|
|
1470 |
hideLoading();
|
|
|
1471 |
const loadingContainer = document.createElement('div');
|
1472 |
loadingContainer.className = 'loading-container fade-in';
|
1473 |
loadingContainer.id = 'loadingContainer';
|
|
|
1498 |
|
1499 |
if (progress >= 0) {
|
1500 |
let progressBar = $id('progressBar');
|
|
|
1501 |
if (!progressBar) {
|
1502 |
const loadingContainer = $id('loadingContainer');
|
1503 |
if (loadingContainer) {
|
|
|
1508 |
progressBar = $id('progressBar');
|
1509 |
}
|
1510 |
} else {
|
1511 |
+
progressBar.style.width = \`\${progress}%\`;
|
1512 |
}
|
1513 |
}
|
1514 |
}
|
|
|
1521 |
}
|
1522 |
|
1523 |
function showError(message) {
|
|
|
1524 |
const existingError = $id('errorContainer');
|
1525 |
if (existingError) {
|
1526 |
existingError.remove();
|
|
|
1536 |
|
1537 |
document.body.appendChild(errorContainer);
|
1538 |
|
|
|
1539 |
$id('errorCloseBtn').onclick = () => {
|
1540 |
errorContainer.remove();
|
1541 |
};
|
1542 |
|
|
|
1543 |
setTimeout(() => {
|
1544 |
if ($id('errorContainer')) {
|
1545 |
$id('errorContainer').remove();
|
|
|
1547 |
}, 5000);
|
1548 |
}
|
1549 |
|
1550 |
+
window.addEventListener('DOMContentLoaded', () => {
|
1551 |
+
// ์
๋ก๋ ๋ฒํผ์ด ์ ์ ๋์ํ๋๋ก ์ง์ ์ด๋ฒคํธ ์ค์
|
1552 |
+
setupDirectEvents();
|
1553 |
+
|
1554 |
+
loadServerPDFs();
|
1555 |
+
|
1556 |
+
// ์บ์ ์ํ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ํ์ธ
|
1557 |
+
setInterval(checkCacheStatus, 3000);
|
1558 |
+
});
|
1559 |
</script>
|
1560 |
</body>
|
1561 |
</html>
|
|
|
1566 |
return get_html_content()
|
1567 |
|
1568 |
if __name__ == "__main__":
|
1569 |
+
uvicorn.run("app:app", host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
|