JAMESPARK3 commited on
Commit
70a5dc1
ยท
verified ยท
1 Parent(s): 27177f4

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +496 -0
app.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import time
4
+ import random
5
+ from PIL import Image
6
+ import glob
7
+ from streamlit_autorefresh import st_autorefresh
8
+ import base64
9
+ from streamlit.components.v1 import html
10
+ import io
11
+
12
+ # ํŽ˜์ด์ง€ ์„ค์ •
13
+ st.set_page_config(
14
+ page_title="๋””์Šคํ”Œ๋ ˆ์ด ์ปจํ…์ธ  ๊ด€๋ฆฌ",
15
+ page_icon="๐Ÿ–ผ๏ธ",
16
+ layout="wide",
17
+ initial_sidebar_state="collapsed"
18
+ )
19
+
20
+ # CSS ์Šคํƒ€์ผ ์ •์˜
21
+ st.markdown("""
22
+ <style>
23
+ .main-content {
24
+ padding: 0;
25
+ max-width: 100%;
26
+ }
27
+ .stApp {
28
+ margin: 0;
29
+ padding: 0;
30
+ }
31
+
32
+ /* ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์Šคํƒ€์ผ๋กœ ์ˆ˜์ • */
33
+ .full-view {
34
+ width: 100vw;
35
+ height: 100vh;
36
+ position: fixed;
37
+ top: 0;
38
+ left: 0;
39
+ z-index: 9999;
40
+ background-color: #000;
41
+ background-size: cover;
42
+ background-position: center;
43
+ background-repeat: no-repeat;
44
+ display: flex;
45
+ flex-direction: column;
46
+ justify-content: flex-end;
47
+ align-items: flex-end;
48
+ padding: 20px;
49
+ }
50
+
51
+ /* ์ „ํ™˜ ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
52
+ .view-toggle {
53
+ background: rgba(0,0,0,0.5);
54
+ color: white;
55
+ border: none;
56
+ padding: 5px 10px;
57
+ margin: 5px;
58
+ border-radius: 4px;
59
+ cursor: pointer;
60
+ font-size: 14px;
61
+ }
62
+
63
+ /* ํ•˜๋‹จ ์ปจํŠธ๋กค ํŒจ๋„ */
64
+ .controls-panel {
65
+ display: flex;
66
+ gap: 10px;
67
+ margin-top: 10px;
68
+ }
69
+
70
+ /* ์ธ๋„ค์ผ ์Šคํƒ€์ผ */
71
+ .thumbnail-container {
72
+ display: flex;
73
+ align-items: center;
74
+ margin-bottom: 5px;
75
+ }
76
+ .thumbnail-container img {
77
+ max-width: 60px;
78
+ max-height: 60px;
79
+ object-fit: contain;
80
+ margin-right: 10px;
81
+ border-radius: 4px;
82
+ border: 1px solid #ddd;
83
+ }
84
+
85
+ /* ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ˆ˜์ • */
86
+ @keyframes fadeIn {
87
+ 0% { opacity: 0; }
88
+ 100% { opacity: 1; }
89
+ }
90
+ @keyframes zoomIn {
91
+ 0% { transform: scale(0.8); opacity: 0; }
92
+ 100% { transform: scale(1); opacity: 1; }
93
+ }
94
+ @keyframes slideIn {
95
+ 0% { transform: translateY(-50px); opacity: 0; }
96
+ 100% { transform: translateY(0); opacity: 1; }
97
+ }
98
+ @keyframes rotateIn {
99
+ 0% { transform: rotate(-10deg) scale(0.8); opacity: 0; }
100
+ 100% { transform: rotate(0) scale(1); opacity: 1; }
101
+ }
102
+
103
+ .animate-fade {
104
+ animation: fadeIn 1s ease-out forwards;
105
+ }
106
+ .animate-zoom {
107
+ animation: zoomIn 1s ease-out forwards;
108
+ }
109
+ .animate-slide {
110
+ animation: slideIn 1s ease-out forwards;
111
+ }
112
+ .animate-rotate {
113
+ animation: rotateIn 1s ease-out forwards;
114
+ }
115
+ </style>
116
+ """, unsafe_allow_html=True)
117
+
118
+ # ์ดˆ๊ธฐ ์„ค์ • ๋ฐ ์ƒํƒœ ๊ด€๋ฆฌ
119
+ if 'contents' not in st.session_state:
120
+ # contents ํด๋” ๋‚ด์˜ ๋ชจ๋“  ์ด๋ฏธ์ง€ ํŒŒ์ผ ๋กœ๋“œ
121
+ if not os.path.exists('contents'):
122
+ os.makedirs('contents')
123
+
124
+ image_files = glob.glob('contents/*.jpg') + glob.glob('contents/*.jpeg') + glob.glob('contents/*.png')
125
+ st.session_state.contents = image_files
126
+
127
+ if 'current_image_index' not in st.session_state:
128
+ st.session_state.current_image_index = 0
129
+
130
+ if 'animation_type' not in st.session_state:
131
+ st.session_state.animation_type = 'animate-fade'
132
+
133
+ if 'activate_fullscreen' not in st.session_state:
134
+ st.session_state.activate_fullscreen = False
135
+
136
+ if 'fit_mode' not in st.session_state:
137
+ st.session_state.fit_mode = 'cover' # ๊ธฐ๋ณธ๊ฐ’์€ ํ™”๋ฉด์— ๊ฝ‰ ์ฐจ๊ฒŒ
138
+
139
+ # ์—ฌ๊ธฐ์— ์ถ”๊ฐ€ํ•˜์„ธ์š”!
140
+ if 'app_loaded' not in st.session_state:
141
+ st.session_state.app_loaded = False
142
+
143
+ # ์Šฌ๋ผ์ด๋“œ์‡ผ ์ž๋™ ์‹œ์ž‘ ๋กœ์ง
144
+ if not st.session_state.app_loaded and not st.session_state.get('preview_mode', False):
145
+ if st.session_state.contents: # ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ž๋™ ์‹œ์ž‘
146
+ st.session_state.preview_mode = True
147
+ st.session_state.preview_start_time = time.time()
148
+ st.session_state.activate_fullscreen = True
149
+ st.session_state.app_loaded = True # ์•ฑ์ด ๋กœ๋“œ๋˜์—ˆ์Œ์„ ํ‘œ์‹œ
150
+
151
+ # ์ด๋ฏธ์ง€ ์ธ๋„ค์ผ ์ƒ์„ฑ ํ•จ์ˆ˜
152
+ def create_thumbnail(image_path, size=(60, 60)):
153
+ try:
154
+ img = Image.open(image_path)
155
+ img.thumbnail(size)
156
+ buffered = io.BytesIO()
157
+ img.save(buffered, format=img.format or "JPEG")
158
+ return buffered.getvalue()
159
+ except Exception as e:
160
+ st.error(f"์ธ๋„ค์ผ ์ƒ์„ฑ ์˜ค๋ฅ˜: {e}")
161
+ return None
162
+
163
+ # ์ด๋ฏธ์ง€ ์ˆœ์„œ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋“ค
164
+ def move_up(index):
165
+ if index > 0:
166
+ st.session_state.contents[index], st.session_state.contents[index-1] = st.session_state.contents[index-1], st.session_state.contents[index]
167
+
168
+ def move_down(index):
169
+ if index < len(st.session_state.contents) - 1:
170
+ st.session_state.contents[index], st.session_state.contents[index+1] = st.session_state.contents[index+1], st.session_state.contents[index]
171
+
172
+ def move_to_top(index):
173
+ if index > 0:
174
+ item = st.session_state.contents.pop(index)
175
+ st.session_state.contents.insert(0, item)
176
+
177
+ def move_to_bottom(index):
178
+ if index < len(st.session_state.contents) - 1:
179
+ item = st.session_state.contents.pop(index)
180
+ st.session_state.contents.append(item)
181
+
182
+ # ์ž๋™ ์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ๋ฅผ ์œ„ํ•œ JavaScript ํ•จ์ˆ˜
183
+ def inject_fullscreen_js():
184
+ js_code = """
185
+ <script>
186
+ // ์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ ํ™œ์„ฑํ™” ํ•จ์ˆ˜
187
+ function activateFullscreen() {
188
+ // ์ „์ฒดํ™”๋ฉด ๋ฒ„ํŠผ ์ฐพ๊ธฐ (Streamlit์˜ ๊ธฐ๋ณธ ์ „์ฒดํ™”๋ฉด ๋ฒ„ํŠผ)
189
+ const fullscreenButton = window.parent.document.querySelector('button[title="View fullscreen"]');
190
+
191
+ if (fullscreenButton) {
192
+ // ๋ฒ„ํŠผ์ด ์žˆ์œผ๋ฉด ํด๋ฆญ
193
+ fullscreenButton.click();
194
+ } else {
195
+ // ๋ฒ„ํŠผ์„ ์ฐพ์„ ์ˆ˜ ์—†์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ € API ์‚ฌ์šฉ
196
+ var elem = document.documentElement;
197
+ if (elem.requestFullscreen) {
198
+ elem.requestFullscreen();
199
+ } else if (elem.mozRequestFullScreen) { /* Firefox */
200
+ elem.mozRequestFullScreen();
201
+ } else if (elem.webkitRequestFullscreen) { /* Chrome, Safari & Opera */
202
+ elem.webkitRequestFullscreen();
203
+ } else if (elem.msRequestFullscreen) { /* IE/Edge */
204
+ elem.msRequestFullscreen();
205
+ }
206
+ }
207
+ }
208
+
209
+ // 1์ดˆ ํ›„ ์ „์ฒดํ™”๋ฉด ํ™œ์„ฑํ™”
210
+ setTimeout(function() {
211
+ activateFullscreen();
212
+ }, 1000);
213
+ </script>
214
+ """
215
+ html(js_code, height=0, width=0)
216
+
217
+ # ์• ๋‹ˆ๋ฉ”์ด์…˜ JS๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ํ•จ์ˆ˜
218
+ def inject_animation_js(animation_type):
219
+ js_code = f"""
220
+ <script>
221
+ // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
222
+ function applyAnimation() {{
223
+ const imageContainer = document.querySelector('.full-view img');
224
+ if (imageContainer) {{
225
+ imageContainer.classList.remove('animate-fade', 'animate-zoom', 'animate-slide', 'animate-rotate');
226
+ void imageContainer.offsetWidth; // ๊ฐ•์ œ ๋ฆฌํ”Œ๋กœ์šฐ
227
+ imageContainer.classList.add('{animation_type}');
228
+ }}
229
+ }}
230
+
231
+ // ํŽ˜์ด์ง€ ๋กœ๋“œ ํ›„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
232
+ setTimeout(applyAnimation, 100);
233
+ </script>
234
+ """
235
+ html(js_code, height=0, width=0)
236
+
237
+ # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํ‘œ์‹œ ๋ฐฉ์‹ ์ „ํ™˜์„ ์œ„ํ•œ JavaScript ํ•จ์ˆ˜
238
+ def inject_background_toggle_js():
239
+ js_code = """
240
+ <script>
241
+ // ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํ‘œ์‹œ ๋ฐฉ์‹ ์ „ํ™˜ ํ•จ์ˆ˜
242
+ function toggleBackgroundFit() {
243
+ const fullView = document.querySelector('.full-view');
244
+ if (fullView) {
245
+ const currentFit = fullView.style.backgroundSize;
246
+ if (currentFit === 'cover') {
247
+ fullView.style.backgroundSize = 'contain';
248
+ document.getElementById('fit-toggle-btn').innerText = 'ํ™”๋ฉด์— ๊ฝ‰ ์ฐจ๊ฒŒ';
249
+ } else {
250
+ fullView.style.backgroundSize = 'cover';
251
+ document.getElementById('fit-toggle-btn').innerText = '์›๋ณธ ๋น„์œจ ์œ ์ง€';
252
+ }
253
+ }
254
+ }
255
+
256
+ // ์ „์—ญ ๋ฒ”์œ„์— ํ•จ์ˆ˜ ๋…ธ์ถœ (window ๊ฐ์ฒด์— ์ถ”๊ฐ€)
257
+ window.applyBackgroundAnimation = function(animationType) {
258
+ const fullView = document.querySelector('.full-view');
259
+ if (fullView) {
260
+ // ๋ชจ๋“  ์• ๋‹ˆ๋ฉ”์ด์…˜ ํด๋ž˜์Šค ์ œ๊ฑฐ
261
+ fullView.classList.remove('animate-fade', 'animate-zoom', 'animate-slide', 'animate-rotate');
262
+
263
+ // ์ƒˆ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํด๋ž˜์Šค ์ถ”๊ฐ€
264
+ void fullView.offsetWidth; // ๊ฐ•์ œ ๋ฆฌํ”Œ๋กœ์šฐ
265
+ fullView.classList.add(animationType);
266
+ }
267
+ }
268
+ </script>
269
+ """
270
+ html(js_code, height=0, width=0)
271
+
272
+
273
+ # ์‚ฌ์ด๋“œ๋ฐ”์— ๊ด€๋ฆฌ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
274
+ with st.sidebar:
275
+ st.title("์ปจํ…์ธ  ๊ด€๋ฆฌ")
276
+
277
+ # ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
278
+ uploaded_files = st.file_uploader("์ด๋ฏธ์ง€ ์ถ”๊ฐ€", type=["jpg", "jpeg", "png"], accept_multiple_files=True)
279
+ if uploaded_files:
280
+ for file in uploaded_files:
281
+ # ํŒŒ์ผ ์ €์žฅ
282
+ file_path = os.path.join('contents', file.name)
283
+ with open(file_path, "wb") as f:
284
+ f.write(file.getbuffer())
285
+
286
+ # ๋ชฉ๋ก์— ์ถ”๊ฐ€ (์ค‘๋ณต ํ™•์ธ)
287
+ if file_path not in st.session_state.contents:
288
+ st.session_state.contents.append(file_path)
289
+
290
+ st.success(f"{len(uploaded_files)}๊ฐœ ์ด๋ฏธ์ง€๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
291
+
292
+ # ํ˜„์žฌ ์ฝ˜ํ…์ธ  ๋ชฉ๋ก ๋ฐ ์‚ญ์ œ ๊ธฐ๋Šฅ
293
+ st.subheader("ํ˜„์žฌ ์ปจํ…์ธ  ๋ชฉ๋ก ๋ฐ ์ˆœ์„œ ๊ด€๋ฆฌ")
294
+
295
+ if not st.session_state.contents:
296
+ st.info("์ปจํ…์ธ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.")
297
+ else:
298
+ # ํ‘œ์‹œ ์ˆœ์„œ ๊ด€๋ฆฌ
299
+ for i, img_path in enumerate(st.session_state.contents):
300
+ img_name = os.path.basename(img_path)
301
+
302
+ # ์ธ๋„ค์ผ๊ณผ ํŒŒ์ผ๋ช…์„ ํฌํ•จํ•œ HTML ์ƒ์„ฑ
303
+ thumbnail_data = create_thumbnail(img_path)
304
+ if thumbnail_data:
305
+ thumbnail_b64 = base64.b64encode(thumbnail_data).decode()
306
+ st.markdown(f"""
307
+ <div class="thumbnail-container">
308
+ <img src="data:image/jpeg;base64,{thumbnail_b64}" alt="{img_name}" />
309
+ <div>{i+1}. {img_name}</div>
310
+ </div>
311
+ """, unsafe_allow_html=True)
312
+ else:
313
+ st.write(f"{i+1}. {img_name}")
314
+
315
+ # ์ˆœ์„œ ๋ฒ„ํŠผ๋“ค
316
+ col1, col2 = st.columns(2)
317
+ with col1:
318
+ if st.button("โ†‘ ์œ„๋กœ", key=f"up_{i}", help="ํ•œ ์œ„์น˜ ์œ„๋กœ ์ด๋™"):
319
+ move_up(i)
320
+ st.rerun()
321
+
322
+ with col2:
323
+ if st.button("โ†“ ์•„๋ž˜๋กœ", key=f"down_{i}", help="ํ•œ ์œ„์น˜ ์•„๋ž˜๋กœ ์ด๋™"):
324
+ move_down(i)
325
+ st.rerun()
326
+
327
+ col3, col4 = st.columns(2)
328
+ with col3:
329
+ if st.button("โค’ ๋งจ์œ„๋กœ", key=f"top_{i}", help="๋งจ ์œ„๋กœ ์ด๋™"):
330
+ move_to_top(i)
331
+ st.rerun()
332
+
333
+ with col4:
334
+ if st.button("โค“ ๋งจ์•„๋ž˜๋กœ", key=f"bottom_{i}", help="๋งจ ์•„๋ž˜๋กœ ์ด๋™"):
335
+ move_to_bottom(i)
336
+ st.rerun()
337
+
338
+ # ์‚ญ์ œ ๋ฒ„ํŠผ
339
+ if st.button("๐Ÿ—‘๏ธ ์‚ญ์ œ", key=f"del_{i}", help="์ด๋ฏธ์ง€ ์‚ญ์ œ"):
340
+ if os.path.exists(img_path):
341
+ # ํŒŒ์ผ ์‚ญ์ œ ์—ฌ๋ถ€ ํ™•์ธ
342
+ if st.session_state.contents.count(img_path) == 1:
343
+ os.remove(img_path)
344
+
345
+ # ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐ
346
+ st.session_state.contents.remove(img_path)
347
+
348
+ # ํ˜„์žฌ ์ด๋ฏธ์ง€ ์ธ๋ฑ์Šค ์กฐ์ •
349
+ if st.session_state.current_image_index >= len(st.session_state.contents):
350
+ st.session_state.current_image_index = 0
351
+
352
+ st.rerun()
353
+
354
+ st.markdown("<hr>", unsafe_allow_html=True)
355
+
356
+ # ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ์„ ํƒ
357
+ st.subheader("ํ™”๋ฉด ์ „ํ™˜ ํšจ๊ณผ")
358
+ animation_options = {
359
+ "animate-fade": "ํŽ˜์ด๋“œ ์ธ",
360
+ "animate-zoom": "์คŒ ์ธ",
361
+ "animate-slide": "์Šฌ๋ผ์ด๋“œ ์ธ",
362
+ "animate-rotate": "ํšŒ์ „ ์ธ"
363
+ }
364
+ selected_animation = st.selectbox(
365
+ "ํ™”๋ฉด ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜",
366
+ options=list(animation_options.keys()),
367
+ format_func=lambda x: animation_options[x],
368
+ index=list(animation_options.keys()).index(st.session_state.animation_type)
369
+ )
370
+ if selected_animation != st.session_state.animation_type:
371
+ st.session_state.animation_type = selected_animation
372
+
373
+ # ์žฌ์ƒ ์„ค์ •
374
+ st.subheader("์žฌ์ƒ ์„ค์ •")
375
+ preview_interval = st.slider("ํ‘œ์‹œ ๊ฐ„๊ฒฉ (์ดˆ)", 2, 10, 5)
376
+
377
+ # ํ‘œ์‹œ ์˜ต์…˜
378
+ fit_options = {"cover": "ํ™”๋ฉด์— ๊ฝ‰ ์ฐจ๊ฒŒ", "contain": "์›๋ณธ ๋น„์œจ ์œ ์ง€"}
379
+ selected_fit = st.selectbox(
380
+ "์ด๋ฏธ์ง€ ํ‘œ์‹œ ๋ฐฉ์‹",
381
+ options=list(fit_options.keys()),
382
+ format_func=lambda x: fit_options[x],
383
+ index=list(fit_options.keys()).index(st.session_state.fit_mode)
384
+ )
385
+ if selected_fit != st.session_state.fit_mode:
386
+ st.session_state.fit_mode = selected_fit
387
+
388
+ # ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์‹œ์ž‘ ๋ฒ„ํŠผ
389
+ if st.button("์ „์ฒดํ™”๋ฉด ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์‹œ์ž‘", use_container_width=True):
390
+ st.session_state.preview_mode = True
391
+ st.session_state.preview_start_time = time.time()
392
+ st.session_state.activate_fullscreen = True
393
+ st.rerun()
394
+
395
+ # ์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ ํ™œ์„ฑํ™” (์กฐ๊ฑด๋ถ€)
396
+ if st.session_state.get('activate_fullscreen', False):
397
+ inject_fullscreen_js()
398
+ st.session_state.activate_fullscreen = False # ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋„๋ก ๋ฆฌ์…‹
399
+
400
+ # ๋ฉ”์ธ ์ปจํ…์ธ  ์˜์—ญ
401
+ if st.session_state.get('preview_mode', False):
402
+ # ์ž๋™ ์ƒˆ๋กœ๊ณ ์นจ์„ ์œ„ํ•œ ์นด์šดํ„ฐ
403
+ count = st_autorefresh(interval=preview_interval * 1000, key="refreshInterval")
404
+
405
+ # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ „ํ™˜ JS ์‚ฝ์ž… (๋จผ์ € ์‹คํ–‰๋˜์–ด์•ผ ํ•จ)
406
+ inject_background_toggle_js()
407
+
408
+ # ํ˜„์žฌ ์ธ๋ฑ์Šค ์—…๋ฐ์ดํŠธ
409
+ if count > 0 and st.session_state.contents:
410
+ st.session_state.current_image_index = (st.session_state.current_image_index + 1) % len(st.session_state.contents)
411
+
412
+ # ํ™”๋ฉด์— ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋กœ ํ‘œ์‹œ
413
+ if st.session_state.contents:
414
+ full_img_path = st.session_state.contents[st.session_state.current_image_index]
415
+
416
+ # ์›น์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ด๋ฏธ์ง€ URL๋กœ ๋ณ€ํ™˜ (Base64 ์ธ์ฝ”๋”ฉ ์‚ฌ์šฉ)
417
+ with open(full_img_path, "rb") as img_file:
418
+ img_data = base64.b64encode(img_file.read()).decode()
419
+
420
+ # ์ด๋ฏธ์ง€ ํ™•์žฅ์ž ๊ฐ€์ ธ์˜ค๊ธฐ (๊ธฐ๋ณธ๊ฐ’์€ jpeg)
421
+ file_ext = os.path.splitext(full_img_path)[1][1:] or "jpeg"
422
+
423
+ # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์„ค์ •๊ณผ ์ปจํŠธ๋กค ๋ฒ„ํŠผ์ด ํฌํ•จ๋œ HTML ์ƒ์„ฑ
424
+ background_html = f"""
425
+ <div class="full-view animate-{st.session_state.animation_type}"
426
+ style="background-image: url('data:image/{file_ext};base64,{img_data}');
427
+ background-size: {st.session_state.fit_mode};">
428
+ <div class="controls-panel">
429
+ <button id="fit-toggle-btn" class="view-toggle" onclick="toggleBackgroundFit()">
430
+ {'์›๋ณธ ๋น„์œจ ์œ ์ง€' if st.session_state.fit_mode == 'cover' else 'ํ™”๋ฉด์— ๊ฝ‰ ์ฐจ๊ฒŒ'}
431
+ </button>
432
+ <button id="exit-btn" class="view-toggle" onclick="window.location.href=window.location.pathname">
433
+ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ข…๋ฃŒ
434
+ </button>
435
+ </div>
436
+ </div>
437
+ <script>
438
+ // ์ „์ฒดํ™”๋ฉด ํ™œ์„ฑํ™”
439
+ setTimeout(function() {{
440
+ var elem = document.documentElement;
441
+ if (elem.requestFullscreen) {{
442
+ elem.requestFullscreen();
443
+ }} else if (elem.mozRequestFullScreen) {{
444
+ elem.mozRequestFullScreen();
445
+ }} else if (elem.webkitRequestFullscreen) {{
446
+ elem.webkitRequestFullscreen();
447
+ }} else if (elem.msRequestFullscreen) {{
448
+ elem.msRequestFullscreen();
449
+ }}
450
+ }}, 1000);
451
+
452
+ // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ (window ๊ฐ์ฒด์—์„œ ํ•จ์ˆ˜ ํ˜ธ์ถœ)
453
+ setTimeout(function() {{
454
+ if (typeof window.applyBackgroundAnimation === 'function') {{
455
+ window.applyBackgroundAnimation('animate-{st.session_state.animation_type}');
456
+ }}
457
+ }}, 1500);
458
+ </script>
459
+ """
460
+
461
+ # HTML ์ง์ ‘ ์‚ฝ์ž…
462
+ st.markdown(background_html, unsafe_allow_html=True)
463
+
464
+ # Streamlit ์š”์†Œ ์ˆจ๊ธฐ๊ธฐ
465
+ st.markdown("""
466
+ <style>
467
+ #MainMenu {visibility: hidden;}
468
+ header {visibility: hidden;}
469
+ footer {visibility: hidden;}
470
+ </style>
471
+ """, unsafe_allow_html=True)
472
+
473
+
474
+ else:
475
+ st.info("ํ‘œ์‹œํ•  ์ด๋ฏธ์ง€๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.")
476
+ if st.button("๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ข…๋ฃŒ"):
477
+ st.session_state.preview_mode = False
478
+ st.rerun()
479
+
480
+ else:
481
+ # ์ผ๋ฐ˜ ๋ชจ๋“œ - ์ผ๋ฐ˜ ๊ด€๋ฆฌ ํ™”๋ฉด
482
+ st.title("๋””์Šคํ”Œ๋ ˆ์ด ์ปจํ…์ธ  ๊ด€๋ฆฌ")
483
+
484
+ if not st.session_state.contents:
485
+ st.info("์‚ฌ์ด๋“œ๋ฐ”์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.")
486
+ else:
487
+ st.write("์‚ฌ์ด๋“œ๋ฐ”์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์•„๋ž˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์ „์ฒดํ™”๋ฉด ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์‹œ์ž‘ํ•˜์„ธ์š”.")
488
+
489
+ # ์ด๋ฏธ์ง€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ทธ๋ฆฌ๋“œ
490
+ cols = st.columns(4)
491
+ for i, img_path in enumerate(st.session_state.contents):
492
+ if i < len(cols):
493
+ cols[i].image(img_path, caption=os.path.basename(img_path), width=200)
494
+ else:
495
+ break
496
+