Scalino84 commited on
Commit
9113e89
·
verified ·
1 Parent(s): bf9cf56

Upload /bck/templates/archive.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. bck/templates/archive.html +650 -0
bck/templates/archive.html ADDED
@@ -0,0 +1,650 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Flux Bildarchiv{% endblock %}
4
+
5
+ {% block content %}
6
+ <h1>Flux Bildarchiv</h1>
7
+
8
+ <!-- Suche -->
9
+ <div class="mb-3 search-container">
10
+ <form id="searchForm" action="/archive" method="get" class="d-flex flex-column">
11
+ <div class="mb-3 w-100">
12
+ <label for="search" class="form-label">Suche:</label>
13
+ <input type="text" class="form-control" id="search" name="search" value="{{ search_query }}">
14
+ </div>
15
+ <div class="d-flex flex-wrap mt-3">
16
+ <button type="submit" class="btn btn-primary flex-fill" style="width: 33.33%;">Suchen</button>
17
+ <button type="reset" class="btn btn-secondary flex-fill ms-2" style="width: 33.33%;">Zurücksetzen</button>
18
+ </div>
19
+ </form>
20
+ </div>
21
+
22
+ <!-- Filter Accordion -->
23
+ <div class="accordion" id="filterAccordion">
24
+ <div class="accordion-item">
25
+ <h2 class="accordion-header" id="headingOne">
26
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
27
+ Filteroptionen
28
+ </button>
29
+ </h2>
30
+ <div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-bs-parent="#filterAccordion">
31
+ <div class="accordion-body">
32
+ <form id="filterForm" action="/archive" method="get" class="d-flex flex-column flex-md-row flex-wrap">
33
+ <div class="mb-3 flex-grow-1 me-md-3">
34
+ <label for="album_filter" class="form-label">Album:</label>
35
+ <select class="form-control" id="album_filter" name="album">
36
+ <option value="">Alle</option>
37
+ {% for album in albums %}
38
+ <option value="{{ album[0] }}" {% if album[0] == selected_album %}selected{% endif %}>{{ album[1] }}</option>
39
+ {% endfor %}
40
+ </select>
41
+ </div>
42
+ <div class="mb-3 flex-grow-1 me-md-3">
43
+ <label for="category_filter" class="form-label">Kategorie:</label>
44
+ <select class="form-control" id="category_filter" name="category" multiple>
45
+ <option value="">Alle</option>
46
+ {% for category in categories %}
47
+ <option value="{{ category[0] }}" {% if category[0] in selected_categories %}selected{% endif %}>{{ category[1] }}</option>
48
+ {% endfor %}
49
+ </select>
50
+ </div>
51
+ <div class="mb-3 flex-grow-1">
52
+ <label for="items_per_page" class="form-label">Bilder pro Seite:</label>
53
+ <select class="form-control" id="items_per_page" name="items_per_page">
54
+ <option value="15" {% if items_per_page == 15 %}selected{% endif %}>15</option>
55
+ <option value="30" {% if items_per_page == 30 %}selected{% endif %}>30</option>
56
+ <option value="50" {% if items_per_page == 50 %}selected{% endif %}>50</option>
57
+ <option value="75" {% if items_per_page == 75 %}selected{% endif %}>75</option>
58
+ <option value="100" {% if items_per_page == 100 %}selected{% endif %}>100</option>
59
+ </select>
60
+ </div>
61
+ <input type="hidden" name="page" value="{{ page }}" />
62
+ <div class="d-flex flex-wrap mt-3">
63
+ <button type="submit" class="btn btn-primary flex-fill" style="width: 33.34%;">Filtern</button>
64
+ </div>
65
+ </form>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <!-- Archiv Anzeige -->
72
+ <div id="archive" class="container-fluid">
73
+ <!-- Optionen für Alle auswählen und Bearbeitung -->
74
+ <div class="d-flex justify-content-between align-items-center mb-2">
75
+ <div>
76
+ <input type="checkbox" id="selectAll" /> Alle auswählen
77
+ </div>
78
+ <div class="d-flex align-items-center">
79
+ <button id="thumbgalleryBtn" class="btn btn-secondary me-2">Thumbgallery</button>
80
+ <button id="slideshowBtn" class="btn btn-secondary me-2">Slideshow</button>
81
+ <select id="gridLayout" class="form-select me-2" style="width: auto;">
82
+ <option value="2">2 nebeneinander</option>
83
+ <option value="3">3 nebeneinander</option>
84
+ <option value="4">4 nebeneinander</option>
85
+ <option value="5">5 nebeneinander</option>
86
+ <option value="6">6 nebeneinander</option>
87
+ </select>
88
+ <div class="dropdown">
89
+ <button class="btn btn-secondary dropdown-toggle" type="button" id="actionMenu" data-bs-toggle="dropdown" aria-expanded="false">
90
+ Optionen
91
+ </button>
92
+ <ul class="dropdown-menu" aria-labelledby="actionMenu">
93
+ <li><a class="dropdown-item" href="#" id="deleteSelected">Löschen</a></li>
94
+ <li><a class="dropdown-item" href="#" id="addToCategory">Zu Kategorie hinzufügen</a></li>
95
+ <li><a class="dropdown-item" href="#" id="addToAlbum">Zu Album hinzufügen</a></li>
96
+ <li><a class="dropdown-item" href="#" id="downloadSelected">Aktuelle Auswahl downloaden</a></li>
97
+ </ul>
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- Bildgrid -->
103
+ <div class="row" id="imageGrid">
104
+ {% for log in logs %}
105
+ <div class="col-12 col-md-6 col-lg-4 col-xl-3 col-xxl-2">
106
+ <div class="card mb-3 custom-bg">
107
+ <div class="card-body p-0 position-relative">
108
+ <img src="{{ log.output_file }}"
109
+ class="img-fluid image-thumbnail"
110
+ alt="Generiertes Bild"
111
+ data-id="{{ log.id }}"
112
+ data-filename="{{ log.output_file.split('/')[-1] }}"
113
+ data-format="{{ log.output_file.split('.')[-1] }}"
114
+ data-timestamp="{{ log.timestamp }}"
115
+ data-album="{{ log.album }}"
116
+ data-category="{{ log.category }}"
117
+ data-prompt="{{ log.prompt }}"
118
+ data-optimized_prompt="{{ log.optimized_prompt }}">
119
+ <input type="checkbox" class="form-check-input select-item position-absolute top-0 end-0 m-2">
120
+ </div>
121
+ </div>
122
+ </div>
123
+ {% endfor %}
124
+ </div>
125
+ </div>
126
+
127
+ <!-- Paginierung -->
128
+ <div class="d-flex justify-content-center mt-4">
129
+ {% if page > 1 %}
130
+ <a class="btn btn-secondary me-2" href="?page={{ page - 1 }}&items_per_page={{ items_per_page }}{% if search_query %}&search={{ search_query }}{% endif %}{% if selected_album %}&album={{ selected_album }}{% endif %}{% if selected_categories %}&category={{ selected_categories | join(',') }}{% endif %}">Vorherige</a>
131
+ {% endif %}
132
+ {% if logs|length == items_per_page %}
133
+ <a class="btn btn-secondary" href="?page={{ page + 1 }}&items_per_page={{ items_per_page }}{% if search_query %}&search={{ search_query }}{% endif %}{% if selected_album %}&album={{ selected_album }}{% endif %}{% if selected_categories %}&category={{ selected_categories | join(',') }}{% endif %}">Nächste</a>
134
+ {% endif %}
135
+ </div>
136
+
137
+ <!-- "Nach oben"-Button -->
138
+ <button id="scrollTopBtn" class="btn btn-primary" style="display: none; position: fixed; bottom: 20px; right: 20px; width: 100px; z-index: 99;">
139
+ Nach oben
140
+ </button>
141
+
142
+ <!-- Bild-Detail Modal -->
143
+ <div id="imageModal" class="modal fade" tabindex="-1" aria-labelledby="imageModalLabel" aria-hidden="true">
144
+ <div class="modal-dialog modal-lg">
145
+ <div class="modal-content">
146
+ <div class="modal-header">
147
+ <h5 class="modal-title" id="imageModalLabel">Bilddetails</h5>
148
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
149
+ </div>
150
+ <div class="modal-body">
151
+ <div class="image-container" style="cursor: pointer;">
152
+ <img id="modalImage" src="" class="img-fluid mb-3" alt="Bild">
153
+ </div>
154
+ <div class="image-details">
155
+ <p><strong>Dateiname:</strong> <span id="modalFilename"></span></p>
156
+ <p><strong>Bildformat:</strong> <span id="modalFormat"></span></p>
157
+ <p><strong>Datum:</strong> <span id="modalTimestamp"></span></p>
158
+ <p><strong>Album:</strong> <span id="modalAlbum"></span></p>
159
+ <p><strong>Kategorie:</strong> <span id="modalCategory"></span></p>
160
+ <p><strong>Eingabeaufforderung:</strong> <span id="modalPrompt"></span></p>
161
+ <p><strong>Optimierte Eingabeaufforderung:</strong> <span id="modalOptimizedPrompt"></span></p>
162
+ </div>
163
+ </div>
164
+ <div class="modal-footer">
165
+ <button type="button" class="btn btn-primary" id="modalDownloadBtn">
166
+ <i class="fas fa-download"></i> Download
167
+ </button>
168
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+
174
+ <!-- Thumbnail-Galerie Modal -->
175
+ <div id="thumbGalleryModal" class="modal fade" tabindex="-1">
176
+ <div class="modal-dialog modal-lg">
177
+ <div class="modal-content">
178
+ <div class="modal-header">
179
+ <h5 class="modal-title">Thumbnail-Galerie</h5>
180
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
181
+ </div>
182
+ <div class="modal-body">
183
+ <div id="thumbGalleryContainer" class="d-flex flex-wrap justify-content-center">
184
+ </div>
185
+ </div>
186
+ <div class="modal-footer">
187
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </div>
192
+
193
+ <!-- Slideshow Modal -->
194
+ <div id="slideshowModal" class="modal fade" tabindex="-1">
195
+ <div class="modal-dialog modal-lg">
196
+ <div class="modal-content">
197
+ <div class="modal-header">
198
+ <h5 class="modal-title">Diashow</h5>
199
+ <div class="btn-group ms-auto me-2">
200
+ <button class="btn btn-primary btn-sm" id="playSlideshow">
201
+ <i class="fas fa-play"></i>
202
+ </button>
203
+ <button class="btn btn-primary btn-sm" id="pauseSlideshow" style="display: none;">
204
+ <i class="fas fa-pause"></i>
205
+ </button>
206
+ <button class="btn btn-primary btn-sm" id="fullscreenBtn">
207
+ <i class="fas fa-expand"></i>
208
+ </button>
209
+ <button class="btn btn-primary btn-sm" id="downloadCurrentSlide">
210
+ <i class="fas fa-download"></i>
211
+ </button>
212
+ </div>
213
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
214
+ </div>
215
+ <div class="modal-body">
216
+ <div id="carouselExampleControls" class="carousel slide" data-bs-interval="false">
217
+ <div id="slideshowContainer" class="carousel-inner"></div>
218
+ <button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleControls" data-bs-slide="prev">
219
+ <span class="carousel-control-prev-icon" aria-hidden="true"></span>
220
+ <span class="visually-hidden">Vorherige</span>
221
+ </button>
222
+ <button class="carousel-control-next" type="button" data-bs-target="#carouselExampleControls" data-bs-slide="next">
223
+ <span class="carousel-control-next-icon" aria-hidden="true"></span>
224
+ <span class="visually-hidden">Nächste</span>
225
+ </button>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </div>
231
+
232
+ <!-- Modal für Zuweisung zu Album/Kategorie -->
233
+ <div id="assignAlbumModal" class="modal fade" tabindex="-1">
234
+ <div class="modal-dialog">
235
+ <div class="modal-content">
236
+ <div class="modal-header">
237
+ <h5 class="modal-title">Zu Album hinzufügen</h5>
238
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
239
+ </div>
240
+ <div class="modal-body">
241
+ <select class="form-control" id="albumSelect">
242
+ {% for album in albums %}
243
+ <option value="{{ album[0] }}">{{ album[1] }}</option>
244
+ {% endfor %}
245
+ </select>
246
+ </div>
247
+ <div class="modal-footer">
248
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
249
+ <button type="button" class="btn btn-primary" id="assignAlbumBtn">Hinzufügen</button>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+
255
+ <div id="assignCategoryModal" class="modal fade" tabindex="-1">
256
+ <div class="modal-dialog">
257
+ <div class="modal-content">
258
+ <div class="modal-header">
259
+ <h5 class="modal-title">Zu Kategorie hinzufügen</h5>
260
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
261
+ </div>
262
+ <div class="modal-body">
263
+ <select class="form-control" id="categorySelect" multiple>
264
+ {% for category in categories %}
265
+ <option value="{{ category[0] }}">{{ category[1] }}</option>
266
+ {% endfor %}
267
+ </select>
268
+ </div>
269
+ <div class="modal-footer">
270
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
271
+ <button type="button" class="btn btn-primary" id="assignCategoryBtn">Hinzufügen</button>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ </div>
276
+
277
+ <!-- CSS Styles -->
278
+ <style>
279
+ .image-container {
280
+ position: relative;
281
+ text-align: center;
282
+ max-height: 80vh;
283
+ overflow: auto;
284
+ }
285
+
286
+ .image-container img {
287
+ max-width: 100%;
288
+ height: auto;
289
+ transition: transform 0.2s;
290
+ }
291
+
292
+ .image-container img:hover {
293
+ transform: scale(1.02);
294
+ }
295
+
296
+ .thumb-container {
297
+ position: relative;
298
+ display: inline-block;
299
+ }
300
+
301
+ .download-thumb {
302
+ position: absolute;
303
+ bottom: 5px;
304
+ right: 5px;
305
+ opacity: 0;
306
+ transition: opacity 0.3s;
307
+ }
308
+
309
+ .thumb-container:hover .download-thumb {
310
+ opacity: 1;
311
+ }
312
+
313
+ .carousel-item img {
314
+ max-height: 80vh;
315
+ object-fit: contain;
316
+ }
317
+
318
+ #slideshowModal.fullscreen .modal-dialog {
319
+ max-width: 100%;
320
+ margin: 0;
321
+ height: 100vh;
322
+ }
323
+
324
+ #slideshowModal.fullscreen .modal-content {
325
+ height: 100%;
326
+ border: none;
327
+ border-radius: 0;
328
+ }
329
+
330
+ .carousel-control-prev,
331
+ .carousel-control-next {
332
+ width: 10%;
333
+ opacity: 0;
334
+ transition: opacity 0.3s;
335
+ }
336
+
337
+ .carousel:hover .carousel-control-prev,
338
+ .carousel:hover .carousel-control-next {
339
+ opacity: 0.5;
340
+ }
341
+
342
+ .modal-dialog {
343
+ max-width: 90vw;
344
+ margin: 1.75rem auto;
345
+ }
346
+
347
+ .image-details {
348
+ margin-top: 1rem;
349
+ padding: 1rem;
350
+ background-color: rgba(0,0,0,0.02);
351
+ border-radius: 4px;
352
+ }
353
+
354
+ .modal-footer {
355
+ justify-content: space-between;
356
+ }
357
+ </style>
358
+
359
+ <!-- {% block scripts %}
360
+ <script src="/static/script.js"></script>
361
+ {% endblock %} -->
362
+ <script>
363
+ document.addEventListener('DOMContentLoaded', function () {
364
+ // Hilfsfunktion für Einzelbild-Download
365
+ async function downloadSingleImage(filename) {
366
+ try {
367
+ const response = await fetch('/flux-pics/single', {
368
+ method: 'POST',
369
+ headers: { 'Content-Type': 'application/json' },
370
+ body: JSON.stringify({ filename })
371
+ });
372
+
373
+ if (!response.ok) {
374
+ const errorText = await response.text();
375
+ console.error('Server Error:', errorText);
376
+ throw new Error(`HTTP error! status: ${response.status}`);
377
+ }
378
+
379
+ const blob = await response.blob();
380
+ const url = window.URL.createObjectURL(blob);
381
+ const a = document.createElement('a');
382
+ a.style.display = 'none';
383
+ a.href = url;
384
+ a.download = filename;
385
+ document.body.appendChild(a);
386
+ a.click();
387
+ window.URL.revokeObjectURL(url);
388
+ } catch (error) {
389
+ console.error('Fehler beim Download:', error);
390
+ alert('Ein Fehler ist aufgetreten: ' + error.message);
391
+ }
392
+ }
393
+
394
+ // Function to open modal with image details
395
+ function openImageModal(img) {
396
+ const modal = new bootstrap.Modal(document.getElementById('imageModal'));
397
+ const modalImg = document.getElementById('modalImage');
398
+ const filename = img.dataset.filename;
399
+
400
+ modalImg.src = img.src;
401
+ document.getElementById('modalFilename').textContent = filename;
402
+ document.getElementById('modalFormat').textContent = img.dataset.format;
403
+ document.getElementById('modalTimestamp').textContent = img.dataset.timestamp;
404
+ document.getElementById('modalAlbum').textContent = img.dataset.album;
405
+ document.getElementById('modalCategory').textContent = img.dataset.category;
406
+ document.getElementById('modalPrompt').textContent = img.dataset.prompt;
407
+ document.getElementById('modalOptimizedPrompt').textContent = img.dataset.optimized_prompt;
408
+
409
+ // Click-to-Close Funktionalität
410
+ document.querySelector('.image-container').onclick = function(e) {
411
+ if (e.target === modalImg) {
412
+ modal.hide();
413
+ }
414
+ };
415
+
416
+ // Download-Button Funktionalität
417
+ document.getElementById('modalDownloadBtn').onclick = async function() {
418
+ await downloadSingleImage(filename);
419
+ };
420
+
421
+ modal.show();
422
+ }
423
+
424
+ // Event listener for image click
425
+ document.querySelectorAll('.image-thumbnail').forEach(function(img) {
426
+ img.addEventListener('click', function() {
427
+ openImageModal(this);
428
+ });
429
+ });
430
+
431
+ // Überarbeitete getSelectedImages Funktion
432
+ function getSelectedImages() {
433
+ const selectedImages = [];
434
+ const checkboxes = document.querySelectorAll('.select-item:checked');
435
+ console.log(`Found ${checkboxes.length} selected images`);
436
+
437
+ checkboxes.forEach(checkbox => {
438
+ const img = checkbox.closest('.card').querySelector('img');
439
+ if (img && img.getAttribute('data-filename')) {
440
+ selectedImages.push(img.getAttribute('data-filename'));
441
+ } else {
442
+ console.warn('Missing image or filename for selected checkbox');
443
+ }
444
+ });
445
+ return selectedImages;
446
+ }
447
+
448
+ // "Alle auswählen" Checkbox-Logik
449
+ const selectAllCheckbox = document.getElementById('selectAll');
450
+ const itemCheckboxes = document.querySelectorAll('.select-item');
451
+
452
+ selectAllCheckbox.addEventListener('change', function () {
453
+ itemCheckboxes.forEach(checkbox => {
454
+ checkbox.checked = selectAllCheckbox.checked;
455
+ });
456
+ });
457
+
458
+ // Download ausgewählter Bilder
459
+ document.getElementById('downloadSelected').addEventListener('click', async function () {
460
+ const selectedImages = getSelectedImages();
461
+ console.log('Selected images:', selectedImages);
462
+
463
+ if (selectedImages.length === 0) {
464
+ alert('Keine Bilder ausgewählt.');
465
+ return;
466
+ }
467
+
468
+ let downloadType = 'single';
469
+ if (selectedImages.length > 1) {
470
+ const choice = confirm('Möchten Sie die Bilder als ZIP-Datei herunterladen?\nKlicken Sie "OK" für ZIP oder "Abbrechen" für Einzeldownloads.');
471
+ if (choice) {
472
+ downloadType = 'zip';
473
+ }
474
+ }
475
+
476
+ try {
477
+ if (downloadType === 'zip') {
478
+ const response = await fetch('/flux-pics', {
479
+ method: 'POST',
480
+ headers: { 'Content-Type': 'application/json' },
481
+ body: JSON.stringify({ selectedImages })
482
+ });
483
+
484
+ if (!response.ok) {
485
+ const errorText = await response.text();
486
+ console.error('Server Error:', errorText);
487
+ throw new Error(`HTTP error! status: ${response.status}`);
488
+ }
489
+
490
+ const blob = await response.blob();
491
+ const url = window.URL.createObjectURL(blob);
492
+ const a = document.createElement('a');
493
+ a.style.display = 'none';
494
+ a.href = url;
495
+ a.download = 'images.zip';
496
+ document.body.appendChild(a);
497
+ a.click();
498
+ window.URL.revokeObjectURL(url);
499
+ } else {
500
+ for (const filename of selectedImages) {
501
+ await downloadSingleImage(filename);
502
+ await new Promise(resolve => setTimeout(resolve, 500));
503
+ }
504
+ }
505
+ alert('Download erfolgreich abgeschlossen.');
506
+ } catch (error) {
507
+ console.error('Fehler beim Downloaden:', error);
508
+ alert('Ein Fehler ist aufgetreten: ' + error.message);
509
+ }
510
+ });
511
+
512
+ // Thumbnail-Galerie
513
+ document.getElementById('thumbgalleryBtn').addEventListener('click', function () {
514
+ const selectedImages = getSelectedImages();
515
+ if (selectedImages.length === 0) {
516
+ alert('Keine Bilder ausgewählt.');
517
+ return;
518
+ }
519
+
520
+ const galleryModal = new bootstrap.Modal(document.getElementById('thumbGalleryModal'));
521
+ const galleryContainer = document.getElementById('thumbGalleryContainer');
522
+ galleryContainer.innerHTML = '';
523
+
524
+ selectedImages.forEach(filename => {
525
+ const container = document.createElement('div');
526
+ container.className = 'thumb-container m-2';
527
+
528
+ const img = document.createElement('img');
529
+ img.src = `/flux-pics/${filename}`;
530
+ img.className = 'img-thumbnail thumbnail-img';
531
+ img.dataset.filename = filename;
532
+ img.style.maxWidth = '150px';
533
+ img.style.cursor = 'pointer';
534
+
535
+ const downloadBtn = document.createElement('button');
536
+ downloadBtn.className = 'btn btn-sm btn-primary download-thumb';
537
+ downloadBtn.innerHTML = '<i class="fas fa-download"></i>';
538
+
539
+ container.appendChild(img);
540
+ container.appendChild(downloadBtn);
541
+ galleryContainer.appendChild(container);
542
+
543
+ img.addEventListener('click', () => openImageModal(img));
544
+ downloadBtn.addEventListener('click', async () => {
545
+ await downloadSingleImage(filename);
546
+ });
547
+ });
548
+
549
+ galleryModal.show();
550
+ });
551
+
552
+ // Slideshow
553
+ document.getElementById('slideshowBtn').addEventListener('click', function () {
554
+ const selectedImages = getSelectedImages();
555
+ if (selectedImages.length === 0) {
556
+ alert('Keine Bilder ausgewählt.');
557
+ return;
558
+ }
559
+
560
+ const slideshowModal = new bootstrap.Modal(document.getElementById('slideshowModal'));
561
+ const slideshowContainer = document.getElementById('slideshowContainer');
562
+ slideshowContainer.innerHTML = '';
563
+
564
+ let slideshowInterval;
565
+ const slideInterval = 3000; // 3 Sekunden pro Bild
566
+
567
+ selectedImages.forEach((filename, index) => {
568
+ const div = document.createElement('div');
569
+ div.classList.add('carousel-item');
570
+ if (index === 0) div.classList.add('active');
571
+
572
+ const img = document.createElement('img');
573
+ img.src = `/flux-pics/${filename}`;
574
+ img.classList.add('d-block', 'w-100');
575
+ img.dataset.filename = filename;
576
+
577
+ div.appendChild(img);
578
+ slideshowContainer.appendChild(div);
579
+ });
580
+
581
+ const carousel = new bootstrap.Carousel(document.getElementById('carouselExampleControls'), {
582
+ interval: false
583
+ });
584
+
585
+ // Play/Pause Funktionalität
586
+ const playBtn = document.getElementById('playSlideshow');
587
+ const pauseBtn = document.getElementById('pauseSlideshow');
588
+
589
+ playBtn.addEventListener('click', function() {
590
+ slideshowInterval = setInterval(() => {
591
+ carousel.next();
592
+ }, slideInterval);
593
+ playBtn.style.display = 'none';
594
+ pauseBtn.style.display = 'block';
595
+ });
596
+
597
+ pauseBtn.addEventListener('click', function() {
598
+ clearInterval(slideshowInterval);
599
+ pauseBtn.style.display = 'none';
600
+ playBtn.style.display = 'block';
601
+ });
602
+
603
+ // Vollbild Funktionalität
604
+ document.getElementById('fullscreenBtn').addEventListener('click', function() {
605
+ const modalElement = document.getElementById('slideshowModal');
606
+ if (modalElement.requestFullscreen) {
607
+ modalElement.requestFullscreen();
608
+ } else if (modalElement.webkitRequestFullscreen) {
609
+ modalElement.webkitRequestFullscreen();
610
+ } else if (modalElement.msRequestFullscreen) {
611
+ modalElement.msRequestFullscreen();
612
+ }
613
+ });
614
+
615
+ // Download aktuelles Bild
616
+ document.getElementById('downloadCurrentSlide').addEventListener('click', async function() {
617
+ const activeSlide = slideshowContainer.querySelector('.carousel-item.active img');
618
+ if (activeSlide) {
619
+ await downloadSingleImage(activeSlide.dataset.filename);
620
+ }
621
+ });
622
+
623
+ slideshowModal.show();
624
+ });
625
+
626
+ // Grid Layout
627
+ document.getElementById('gridLayout').addEventListener('change', function () {
628
+ const columns = parseInt(this.value);
629
+ const imageGrid = document.getElementById('imageGrid');
630
+ imageGrid.className = `row row-cols-1 row-cols-md-${columns}`;
631
+ });
632
+
633
+ // Nach oben Button
634
+ const scrollTopBtn = document.getElementById('scrollTopBtn');
635
+ window.addEventListener('scroll', function () {
636
+ if (window.scrollY > 300) {
637
+ scrollTopBtn.style.display = 'block';
638
+ } else {
639
+ scrollTopBtn.style.display = 'none';
640
+ }
641
+ });
642
+
643
+ scrollTopBtn.addEventListener('click', function () {
644
+ window.scrollTo({ top: 0, behavior: 'smooth' });
645
+ });
646
+
647
+ });
648
+ </script>
649
+
650
+ {% endblock %}