Cleanup of the front end and backend of the upload function to only allow images and no videos because of space and memory reasons as well as hardening the uploading process.
Release Inventarsystem / release-docker (push) Successful in 2m15s
Release Inventarsystem / release-docker (push) Successful in 2m15s
This commit is contained in:
+26
-125
@@ -5239,7 +5239,7 @@ def debug_favorites():
|
|||||||
def upload_item():
|
def upload_item():
|
||||||
"""
|
"""
|
||||||
Route for adding new items to the inventory.
|
Route for adding new items to the inventory.
|
||||||
Handles file uploads and creates QR codes.
|
Handles image file uploads and creates QR codes.
|
||||||
Enhanced for mobile browser compatibility.
|
Enhanced for mobile browser compatibility.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -5266,9 +5266,9 @@ def upload_item():
|
|||||||
# Detect if request is from mobile device
|
# Detect if request is from mobile device
|
||||||
is_mobile = 'Mobile' in request.headers.get('User-Agent', '')
|
is_mobile = 'Mobile' in request.headers.get('User-Agent', '')
|
||||||
|
|
||||||
# Log mobile request for debugging
|
|
||||||
if is_mobile:
|
if is_mobile:
|
||||||
app.logger.info(f"Mobile upload from {request.headers.get('User-Agent', 'unknown')} by {encrypt_text(username)}")
|
app.logger.info(
|
||||||
|
f"Mobile upload from {request.headers.get('User-Agent', 'unknown')} by {encrypt_text(username)}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Strip whitespace from all text fields
|
# Strip whitespace from all text fields
|
||||||
@@ -5301,46 +5301,30 @@ def upload_item():
|
|||||||
item_count = 1
|
item_count = 1
|
||||||
item_count = max(1, min(item_count, 100))
|
item_count = max(1, min(item_count, 100))
|
||||||
|
|
||||||
# Optional list of per-item codes (one code per line)
|
# Optional list of per-item codes
|
||||||
individual_codes = []
|
individual_codes = []
|
||||||
if individual_codes_raw:
|
if individual_codes_raw:
|
||||||
individual_codes = [c.strip() for c in str(individual_codes_raw).replace(',', '\n').splitlines() if c.strip()]
|
individual_codes = [c.strip() for c in str(individual_codes_raw).replace(',', '\n').splitlines() if
|
||||||
|
c.strip()]
|
||||||
|
|
||||||
# Check if this is a duplication
|
|
||||||
is_duplicating = request.form.get('is_duplicating') == 'true'
|
|
||||||
|
|
||||||
# Get duplicate_images if duplicating
|
|
||||||
duplicate_images = request.form.getlist('duplicate_images') if is_duplicating else []
|
|
||||||
|
|
||||||
# Make sure duplicate_images is always a list, even if there's only one
|
|
||||||
if is_duplicating and duplicate_images and not isinstance(duplicate_images, list):
|
|
||||||
duplicate_images = [duplicate_images]
|
|
||||||
|
|
||||||
# Log details about each image
|
|
||||||
if is_duplicating and duplicate_images:
|
|
||||||
for i, img in enumerate(duplicate_images):
|
|
||||||
print(f"DEBUG: Duplicate image {i+1}/{len(duplicate_images)}: {img}")
|
|
||||||
|
|
||||||
# Get book cover image if downloaded
|
# Get book cover image if downloaded
|
||||||
book_cover_image = request.form.get('book_cover_image')
|
book_cover_image = request.form.get('book_cover_image')
|
||||||
|
|
||||||
# Special handling for mobile browsers that might send data differently
|
# Special handling for mobile browsers
|
||||||
if is_mobile and 'mobile_data' in request.form:
|
if is_mobile and 'mobile_data' in request.form:
|
||||||
try:
|
try:
|
||||||
mobile_data = json.loads(request.form['mobile_data'])
|
mobile_data = json.loads(request.form['mobile_data'])
|
||||||
# Override values with mobile data if available
|
|
||||||
if 'filters' in mobile_data:
|
if 'filters' in mobile_data:
|
||||||
filter_upload = mobile_data.get('filters', [])
|
filter_upload = mobile_data.get('filters', [])
|
||||||
if 'filters2' in mobile_data:
|
if 'filters2' in mobile_data:
|
||||||
filter_upload2 = mobile_data.get('filters2', [])
|
filter_upload2 = mobile_data.get('filters2', [])
|
||||||
if 'filters3' in mobile_data:
|
if 'filters3' in mobile_data:
|
||||||
filter_upload3 = mobile_data.get('filters3', [])
|
filter_upload3 = mobile_data.get('filters3', [])
|
||||||
if 'duplicate_images' in mobile_data and mobile_data['duplicate_images']:
|
|
||||||
duplicate_images = mobile_data.get('duplicate_images', [])
|
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
app.logger.error(f"Error parsing mobile data: {str(e)}")
|
app.logger.error(f"Error parsing mobile data: {str(e)}")
|
||||||
except:
|
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"Upload Form Data Parsing Error: {str(e)}")
|
||||||
flash('Fehler beim Verarbeiten der Formulardaten. Bitte versuchen Sie es erneut.', 'error')
|
flash('Fehler beim Verarbeiten der Formulardaten. Bitte versuchen Sie es erneut.', 'error')
|
||||||
return redirect(url_for(success_redirect_endpoint))
|
return redirect(url_for(success_redirect_endpoint))
|
||||||
|
|
||||||
@@ -5348,7 +5332,7 @@ def upload_item():
|
|||||||
filter_upload = expand_filter_selection(filter_upload, 1)
|
filter_upload = expand_filter_selection(filter_upload, 1)
|
||||||
filter_upload2 = expand_filter_selection(filter_upload2, 2)
|
filter_upload2 = expand_filter_selection(filter_upload2, 2)
|
||||||
|
|
||||||
# Validation
|
# Base Validation
|
||||||
if not name or not ort or not beschreibung:
|
if not name or not ort or not beschreibung:
|
||||||
error_msg = 'Bitte füllen Sie alle erforderlichen Felder aus'
|
error_msg = 'Bitte füllen Sie alle erforderlichen Felder aus'
|
||||||
if is_mobile:
|
if is_mobile:
|
||||||
@@ -5384,9 +5368,8 @@ def upload_item():
|
|||||||
flash(error_msg, 'error')
|
flash(error_msg, 'error')
|
||||||
return redirect(url_for('library_admin'))
|
return redirect(url_for('library_admin'))
|
||||||
|
|
||||||
# Only check for images if not duplicating and no duplicate images provided and no book cover
|
# Strict Image Validation (Since duplication is removed)
|
||||||
# For library mode, skip this check as images come only from ISBN fetch
|
if upload_mode != 'library' and not images and not book_cover_image:
|
||||||
if upload_mode != 'library' and not is_duplicating and not images and not duplicate_images and not book_cover_image:
|
|
||||||
error_msg = 'Bitte laden Sie mindestens ein Bild hoch'
|
error_msg = 'Bitte laden Sie mindestens ein Bild hoch'
|
||||||
if is_mobile:
|
if is_mobile:
|
||||||
return jsonify({'success': False, 'message': error_msg}), 400
|
return jsonify({'success': False, 'message': error_msg}), 400
|
||||||
@@ -5395,28 +5378,20 @@ def upload_item():
|
|||||||
return redirect(url_for(success_redirect_endpoint))
|
return redirect(url_for(success_redirect_endpoint))
|
||||||
|
|
||||||
primary_code_raw = request.form.get('code_4', '').strip()
|
primary_code_raw = request.form.get('code_4', '').strip()
|
||||||
individual_codes_raw = request.form.get('individual_codes', '').strip()
|
|
||||||
|
|
||||||
all_item_codes = []
|
all_item_codes = []
|
||||||
|
|
||||||
if primary_code_raw:
|
if primary_code_raw:
|
||||||
all_item_codes.append(primary_code_raw)
|
all_item_codes.append(primary_code_raw)
|
||||||
|
|
||||||
if individual_codes_raw:
|
if individual_codes:
|
||||||
extra_codes = [c.strip() for c in individual_codes_raw.replace('\r', '').split('\n') if c.strip()]
|
for c in individual_codes:
|
||||||
|
|
||||||
for c in extra_codes:
|
|
||||||
if c not in all_item_codes:
|
if c not in all_item_codes:
|
||||||
all_item_codes.append(c)
|
all_item_codes.append(c)
|
||||||
|
|
||||||
if len(all_item_codes) > 0:
|
if len(all_item_codes) > 0:
|
||||||
item_count = len(all_item_codes)
|
item_count = len(all_item_codes)
|
||||||
else:
|
|
||||||
try:
|
|
||||||
item_count = int(request.form.get('item_count', 1))
|
|
||||||
except ValueError:
|
|
||||||
item_count = 1
|
|
||||||
|
|
||||||
|
# Check unique codes
|
||||||
for code in all_item_codes:
|
for code in all_item_codes:
|
||||||
if not it.is_code_unique(code):
|
if not it.is_code_unique(code):
|
||||||
app.logger.info(f"DEBUG: Code '{code}' is not unique.")
|
app.logger.info(f"DEBUG: Code '{code}' is not unique.")
|
||||||
@@ -5443,14 +5418,11 @@ def upload_item():
|
|||||||
return redirect(url_for(success_redirect_endpoint))
|
return redirect(url_for(success_redirect_endpoint))
|
||||||
|
|
||||||
def generate_unique_batch_code(base_code, position):
|
def generate_unique_batch_code(base_code, position):
|
||||||
"""Generate a unique code for every item in a batch if no specific code is provided."""
|
|
||||||
if not base_code:
|
if not base_code:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
candidate = base_code if position == 1 else f"{base_code}"
|
candidate = base_code if position == 1 else f"{base_code}"
|
||||||
if it.is_code_unique(candidate):
|
if it.is_code_unique(candidate):
|
||||||
return candidate
|
return candidate
|
||||||
|
|
||||||
suffix = 1
|
suffix = 1
|
||||||
while suffix <= 1000:
|
while suffix <= 1000:
|
||||||
alternative = f"{candidate}"
|
alternative = f"{candidate}"
|
||||||
@@ -5465,14 +5437,11 @@ def upload_item():
|
|||||||
skipped_count = 0
|
skipped_count = 0
|
||||||
|
|
||||||
upload_session_id = str(uuid.uuid4())[:8]
|
upload_session_id = str(uuid.uuid4())[:8]
|
||||||
app.logger.info(f"Starting image upload session {upload_session_id} - Files: {len(images)}, User: {encrypt_text(username)}")
|
app.logger.info(
|
||||||
|
f"Starting image upload session {upload_session_id} - Files: {len(images)}, User: {encrypt_text(username)}")
|
||||||
|
|
||||||
|
# 1. Process Manual Image Uploads
|
||||||
for index, image in enumerate(images):
|
for index, image in enumerate(images):
|
||||||
#if upload_mode == 'library':
|
|
||||||
# app.logger.info(f"[Upload {upload_session_id}] Skipping manual upload (Library Mode)")
|
|
||||||
# skipped_count += 1
|
|
||||||
# continue
|
|
||||||
|
|
||||||
if not image or not image.filename:
|
if not image or not image.filename:
|
||||||
skipped_count += 1
|
skipped_count += 1
|
||||||
continue
|
continue
|
||||||
@@ -5490,9 +5459,7 @@ def upload_item():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
secure_name = secure_filename(image.filename)
|
secure_name = secure_filename(image.filename)
|
||||||
|
|
||||||
image.seek(0)
|
image.seek(0)
|
||||||
|
|
||||||
image_bytes = image.read()
|
image_bytes = image.read()
|
||||||
|
|
||||||
if not image_bytes:
|
if not image_bytes:
|
||||||
@@ -5527,7 +5494,6 @@ def upload_item():
|
|||||||
)
|
)
|
||||||
|
|
||||||
image_filenames.append(new_filename)
|
image_filenames.append(new_filename)
|
||||||
|
|
||||||
processed_count += 1
|
processed_count += 1
|
||||||
final_size_kb = len(optimized_io.getvalue()) / 1024
|
final_size_kb = len(optimized_io.getvalue()) / 1024
|
||||||
app.logger.info(
|
app.logger.info(
|
||||||
@@ -5537,79 +5503,13 @@ def upload_item():
|
|||||||
app.logger.error(f"{image_log_prefix} Processing failed: {str(e)}")
|
app.logger.error(f"{image_log_prefix} Processing failed: {str(e)}")
|
||||||
error_count += 1
|
error_count += 1
|
||||||
|
|
||||||
app.logger.info(f"Upload session {upload_session_id} completed: {processed_count} processed, {error_count} errors, {skipped_count} skipped")
|
# 2. Process Book Cover
|
||||||
|
|
||||||
# 1. Duplikate verarbeiten (Direkt via MongoDB / GridFS)
|
|
||||||
if duplicate_images:
|
|
||||||
app.logger.info(f"Processing {len(duplicate_images)} duplicate images from GridFS: {duplicate_images}")
|
|
||||||
duplicate_image_copies = []
|
|
||||||
placeholder_used = False
|
|
||||||
|
|
||||||
for i, dup_img in enumerate(duplicate_images):
|
|
||||||
try:
|
|
||||||
# Suche das Originalbild direkt in der GridFS-Datenbank
|
|
||||||
existing_file = fs.find_one({"filename": dup_img})
|
|
||||||
|
|
||||||
# Fallback: Falls der exakte Name nicht gefunden wird, suche über Regex nach dem Basisnamen
|
|
||||||
if not existing_file:
|
|
||||||
base_name = re.sub(r'_\d+(?=\.[^.]+$)', '', dup_img) # Entfernt Suffixe wie _800
|
|
||||||
base_name_no_ext = os.path.splitext(base_name)[0]
|
|
||||||
|
|
||||||
if len(base_name_no_ext) > 5:
|
|
||||||
app.logger.info(f"Trying regex search for base name: {base_name_no_ext}")
|
|
||||||
existing_file = fs.find_one({"filename": {"$regex": f"^{re.escape(base_name_no_ext)}"}})
|
|
||||||
|
|
||||||
new_filename = f"{uuid.uuid4().hex}_{int(time.time())}.webp"
|
|
||||||
|
|
||||||
if existing_file:
|
|
||||||
app.logger.info(f"Found image in GridFS for {dup_img}. Duplicating...")
|
|
||||||
# Bilddaten in den Speicher laden und direkt als neue Datei in GridFS ablegen
|
|
||||||
image_data = existing_file.read()
|
|
||||||
|
|
||||||
fs.put(
|
|
||||||
image_data,
|
|
||||||
filename=new_filename,
|
|
||||||
content_type=existing_file.content_type,
|
|
||||||
metadata={'original_duplicate_of': dup_img}
|
|
||||||
)
|
|
||||||
duplicate_image_copies.append(new_filename)
|
|
||||||
processed_count += 1
|
|
||||||
else:
|
|
||||||
app.logger.warning(f"Could not find {dup_img} in GridFS. Using placeholder.")
|
|
||||||
placeholder_path = os.path.join(app.static_folder, 'img', 'no-image.png')
|
|
||||||
|
|
||||||
if os.path.exists(placeholder_path):
|
|
||||||
with open(placeholder_path, 'rb') as pf:
|
|
||||||
placeholder_data = pf.read()
|
|
||||||
|
|
||||||
fs.put(
|
|
||||||
placeholder_data,
|
|
||||||
filename=new_filename,
|
|
||||||
content_type='image/png',
|
|
||||||
metadata={'is_placeholder': True}
|
|
||||||
)
|
|
||||||
duplicate_image_copies.append(new_filename)
|
|
||||||
placeholder_used = True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
app.logger.error(f"Error duplicating image {dup_img}: {str(e)}")
|
|
||||||
error_count += 1
|
|
||||||
|
|
||||||
if placeholder_used:
|
|
||||||
app.logger.warning("Used placeholders for some missing images during duplication")
|
|
||||||
|
|
||||||
image_filenames.extend(duplicate_image_copies)
|
|
||||||
|
|
||||||
# 2. Buchcover verarbeiten
|
|
||||||
if book_cover_image:
|
if book_cover_image:
|
||||||
new_filename = f"{uuid.uuid4().hex}_{int(time.time())}_book_cover.webp"
|
new_filename = f"{uuid.uuid4().hex}_{int(time.time())}_book_cover.webp"
|
||||||
|
|
||||||
# Prüfen, ob das Cover noch als lokale temporäre Datei existiert (z.B. von einer API geladen)
|
|
||||||
local_path = os.path.join(app.config.get('UPLOAD_FOLDER', ''), book_cover_image)
|
local_path = os.path.join(app.config.get('UPLOAD_FOLDER', ''), book_cover_image)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if os.path.exists(local_path):
|
if os.path.exists(local_path):
|
||||||
# Lokales Bild einlesen, zu WebP optimieren und in GridFS schieben
|
|
||||||
with Image.open(local_path) as img:
|
with Image.open(local_path) as img:
|
||||||
if img.mode not in ('RGB', 'RGBA'):
|
if img.mode not in ('RGB', 'RGBA'):
|
||||||
img = img.convert('RGBA')
|
img = img.convert('RGBA')
|
||||||
@@ -5627,7 +5527,6 @@ def upload_item():
|
|||||||
image_filenames.append(new_filename)
|
image_filenames.append(new_filename)
|
||||||
app.logger.info(f"Processed local book cover and saved to GridFS: {new_filename}")
|
app.logger.info(f"Processed local book cover and saved to GridFS: {new_filename}")
|
||||||
else:
|
else:
|
||||||
# Falls es bereits in GridFS liegt, einfach duplizieren
|
|
||||||
existing_cover = fs.find_one({"filename": book_cover_image})
|
existing_cover = fs.find_one({"filename": book_cover_image})
|
||||||
if existing_cover:
|
if existing_cover:
|
||||||
fs.put(
|
fs.put(
|
||||||
@@ -5643,7 +5542,10 @@ def upload_item():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.error(f"Error processing book cover {book_cover_image}: {str(e)}")
|
app.logger.error(f"Error processing book cover {book_cover_image}: {str(e)}")
|
||||||
|
|
||||||
# 3. Item-Erstellung (Logik bleibt identisch, da it.add_item nun die GridFS-Filenames erhält)
|
app.logger.info(
|
||||||
|
f"Upload session {upload_session_id} completed: {processed_count} processed, {error_count} errors, {skipped_count} skipped")
|
||||||
|
|
||||||
|
# 3. Item Creation
|
||||||
predefined_locations = it.get_predefined_locations()
|
predefined_locations = it.get_predefined_locations()
|
||||||
if ort and ort not in predefined_locations:
|
if ort and ort not in predefined_locations:
|
||||||
it.add_predefined_location(ort)
|
it.add_predefined_location(ort)
|
||||||
@@ -5669,7 +5571,6 @@ def upload_item():
|
|||||||
|
|
||||||
parent_item_id = str(created_item_ids[0]) if created_item_ids else None
|
parent_item_id = str(created_item_ids[0]) if created_item_ids else None
|
||||||
|
|
||||||
# image_filenames enthält jetzt ausschließlich GridFS-Filenames (oder ObjectIds, je nach deinem Setup)
|
|
||||||
item_id = it.add_item(
|
item_id = it.add_item(
|
||||||
name, ort, beschreibung, image_filenames, filter_upload,
|
name, ort, beschreibung, image_filenames, filter_upload,
|
||||||
filter_upload2, filter_upload3,
|
filter_upload2, filter_upload3,
|
||||||
@@ -5715,8 +5616,8 @@ def upload_item():
|
|||||||
'codes': created_item_ids and [it.get_item(cid).get('Code_4') for cid in created_item_ids] or []
|
'codes': created_item_ids and [it.get_item(cid).get('Code_4') for cid in created_item_ids] or []
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
app.logger.warning('Audit write failed for library_item_created')
|
app.logger.warning(f"Audit write failed for library_item_created: {str(e)}")
|
||||||
|
|
||||||
flash(success_msg, 'success')
|
flash(success_msg, 'success')
|
||||||
return redirect(url_for(success_redirect_endpoint))
|
return redirect(url_for(success_redirect_endpoint))
|
||||||
|
|||||||
@@ -836,7 +836,7 @@
|
|||||||
<!-- Options will be loaded by JavaScript -->
|
<!-- Options will be loaded by JavaScript -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="formupload_admin-group">
|
<div class="form-group">
|
||||||
<label for="filter2-4">Wert 4:</label>
|
<label for="filter2-4">Wert 4:</label>
|
||||||
<select id="filter2-4" name="filter2" class="filter-dropdown-select">
|
<select id="filter2-4" name="filter2" class="filter-dropdown-select">
|
||||||
<option value="">-- Optional --</option>
|
<option value="">-- Optional --</option>
|
||||||
@@ -876,11 +876,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Image upload (hidden for library mode) -->
|
<!-- Image upload -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="images">Bilder/Videos:</label>
|
<label for="images">Bilder:</label>
|
||||||
<input type="file" id="images" name="images" accept=".jpg, .jpeg, .png, .gif, .mp4, .mov, .avi, .mkv, .webm, .flv, .m4v, .3gp" multiple>
|
<input type="file" id="images" name="images" accept=".jpg, .jpeg, .png, .gif" multiple>
|
||||||
<div class="allowed-formats">Erlaubte Formate: JPG, JPEG, PNG, GIF, MP4, MOV, AVI, MKV, WEBM, FLV, M4V, 3GP</div>
|
<div class="allowed-formats">Erlaubte Formate: JPG, JPEG, PNG, GIF</div>
|
||||||
<!-- Add image preview area -->
|
<!-- Add image preview area -->
|
||||||
<div class="image-preview-container" id="image-preview-container"></div>
|
<div class="image-preview-container" id="image-preview-container"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1864,21 +1864,18 @@
|
|||||||
|
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
|
|
||||||
// Validate file types before preview
|
// Validate file types before preview (Strictly Images)
|
||||||
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif',
|
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
|
||||||
'video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/x-matroska',
|
|
||||||
'video/webm', 'video/x-flv', 'video/mp4', 'video/3gpp'];
|
|
||||||
let hasInvalidFile = false;
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
if (!allowedTypes.includes(files[i].type)) {
|
if (!allowedTypes.includes(files[i].type)) {
|
||||||
hasInvalidFile = true;
|
|
||||||
// Clear the file input to prevent submission
|
// Clear the file input to prevent submission
|
||||||
imageInput.value = '';
|
imageInput.value = '';
|
||||||
previewContainer.innerHTML = '<div class="error-message">Fehler: Datei "' + files[i].name + '" hat ein nicht unterstütztes Format. Erlaubte Formate: JPG, JPEG, PNG, GIF, MP4, MOV, AVI, MKV, WEBM, FLV, M4V, 3GP</div>';
|
previewContainer.innerHTML = '<div class="error-message">Fehler: Datei "' + files[i].name + '" hat ein nicht unterstütztes Format. Erlaubte Formate: JPG, JPEG, PNG, GIF</div>';
|
||||||
return; // Stop processing
|
return; // Stop processing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const file = files[i];
|
const file = files[i];
|
||||||
if (file.type.startsWith('image/')) {
|
if (file.type.startsWith('image/')) {
|
||||||
@@ -1893,17 +1890,6 @@
|
|||||||
previewContainer.appendChild(preview);
|
previewContainer.appendChild(preview);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
} else if (file.type.startsWith('video/')) {
|
|
||||||
const preview = document.createElement('div');
|
|
||||||
preview.className = 'image-preview video-preview';
|
|
||||||
preview.innerHTML = `
|
|
||||||
<div class="video-placeholder">
|
|
||||||
<div class="video-icon">🎥</div>
|
|
||||||
<div class="video-name">${file.name}</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="remove-image" onclick="removeImagePreview(this, ${i})">×</button>
|
|
||||||
`;
|
|
||||||
previewContainer.appendChild(preview);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user