Add NAS file manager integration and all pending platform changes
- Installed p7h/nas-file-manager package via private VCS repo - Published config/nas-file-manager.php with super_admin middleware restriction - Added NAS env vars to .env.example - Created admin/nas-storage page with connection info panel and file browser widget - Added NAS Storage link to admin sidebar (super_admin only) - Added SuperAdminController@nasStorage method and admin.nas-storage route - Includes all accumulated branch changes: profile wall, 2FA, audit logs, settings panel, country/phone/timezone components, posts, slideshow, playlist shares, video downloads/shares, comment likes, notifications, social links, and more Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
d44490dfe0
commit
0b2e95ea65
161 files changed
+27496
-7250
No files matched your search
@@ -22,19 +22,20 @@
|
||||
<form id="upload-form-modal" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<!-- Video File -->
|
||||
<!-- Video / Audio File -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-camera-video"></i> Video File *
|
||||
<label class="form-label" id="file-label-modal">
|
||||
<i class="bi bi-camera-video" id="file-label-icon"></i>
|
||||
<span id="file-label-text">Video or Music File *</span>
|
||||
</label>
|
||||
<div id="video-dropzone-modal" class="dropzone-modal">
|
||||
<input type="file" name="video" id="video-modal" accept="video/*" required>
|
||||
<input type="file" name="video" id="video-modal" accept="video/*,audio/mpeg,audio/mp4,audio/aac,audio/flac,audio/wav,.mp3,.m4a,.aac,.flac,.wav" required>
|
||||
<div id="dropzone-default-modal">
|
||||
<div class="dropzone-icon">
|
||||
<i class="bi bi-cloud-arrow-up"></i>
|
||||
</div>
|
||||
<p class="dropzone-title">Click to select or drag video here</p>
|
||||
<p class="dropzone-hint">MP4, MOV, AVI, WebM and more</p>
|
||||
<p class="dropzone-title">Click to select or drag file here</p>
|
||||
<p class="dropzone-hint">Video: MP4, MOV, AVI, WebM — Audio: MP3, M4A, AAC, FLAC</p>
|
||||
</div>
|
||||
<div id="file-info-modal" class="file-info-modal">
|
||||
<div class="file-preview">
|
||||
@@ -71,10 +72,10 @@
|
||||
placeholder="Tell viewers about your video (Markdown supported)"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Thumbnail -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-image"></i> Thumbnail
|
||||
<!-- Thumbnail (video) / Slides (audio) -->
|
||||
<div class="form-group" id="thumbnail-section-modal">
|
||||
<label class="form-label" id="thumbnail-label-modal">
|
||||
<i class="bi bi-image"></i> <span id="thumbnail-label-text">Thumbnail</span>
|
||||
</label>
|
||||
<div id="thumbnail-dropzone-modal" class="dropzone-modal thumbnail-dropzone">
|
||||
<input type="file" name="thumbnail" id="thumbnail-modal" accept="image/*">
|
||||
@@ -82,7 +83,7 @@
|
||||
<div class="dropzone-icon thumbnail-icon">
|
||||
<i class="bi bi-card-image"></i>
|
||||
</div>
|
||||
<p class="dropzone-title">Click to select thumbnail (optional)</p>
|
||||
<p class="dropzone-title" id="thumbnail-dropzone-hint">Click to select thumbnail (optional)</p>
|
||||
</div>
|
||||
<div id="thumbnail-info-modal" class="file-info-modal thumbnail-info">
|
||||
<div class="file-preview thumbnail-preview">
|
||||
@@ -99,6 +100,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slides dropzone (audio only) -->
|
||||
<div class="form-group" id="slides-section-modal" style="display:none;">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-images"></i> Slides <span style="color:#f00;">*</span>
|
||||
<span style="font-weight:400;font-size:12px;color:var(--text-secondary);margin-left:6px;">Select 1–10 images — multiple images will crossfade</span>
|
||||
</label>
|
||||
<div id="slides-dropzone-modal" class="dropzone-modal thumbnail-dropzone" style="min-height:80px;">
|
||||
<input type="file" name="slides[]" id="slides-modal" accept="image/*" multiple style="display:none;">
|
||||
<div id="slides-default-modal">
|
||||
<div class="dropzone-icon thumbnail-icon"><i class="bi bi-images"></i></div>
|
||||
<p class="dropzone-title">Click or drag to add cover images</p>
|
||||
</div>
|
||||
<div id="slides-preview-modal" style="display:none;padding:10px 12px;">
|
||||
<div id="slides-strip-modal" style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px;"></div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;">
|
||||
<span id="slides-count-modal" style="font-size:12px;color:var(--text-secondary);"></span>
|
||||
<button type="button" onclick="removeSlidesModal(event)" style="background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:12px;padding:0;">
|
||||
<i class="bi bi-x-lg"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Video Type -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
@@ -162,6 +187,43 @@
|
||||
<!-- Hidden Shorts -->
|
||||
<input type="hidden" name="is_shorts" id="is_shorts_modal" value="0">
|
||||
|
||||
<!-- Download Access -->
|
||||
<div class="form-group" style="margin-bottom: 16px;">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-download"></i> Who can download?
|
||||
</label>
|
||||
<div class="options-grid" id="download-options-modal">
|
||||
<label class="option-item active" data-download="disabled">
|
||||
<input type="radio" name="download_access" value="disabled" checked>
|
||||
<div class="option-content">
|
||||
<i class="bi bi-slash-circle"></i>
|
||||
<span>Disabled</span>
|
||||
</div>
|
||||
</label>
|
||||
<label class="option-item" data-download="everyone">
|
||||
<input type="radio" name="download_access" value="everyone">
|
||||
<div class="option-content">
|
||||
<i class="bi bi-globe"></i>
|
||||
<span>Everyone</span>
|
||||
</div>
|
||||
</label>
|
||||
<label class="option-item" data-download="registered">
|
||||
<input type="radio" name="download_access" value="registered">
|
||||
<div class="option-content">
|
||||
<i class="bi bi-person-check"></i>
|
||||
<span>Members</span>
|
||||
</div>
|
||||
</label>
|
||||
<label class="option-item" data-download="subscribers">
|
||||
<input type="radio" name="download_access" value="subscribers">
|
||||
<div class="option-content">
|
||||
<i class="bi bi-star"></i>
|
||||
<span>Subscribers</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div id="progress-container-modal" class="progress-container-modal">
|
||||
<div class="progress-bar-wrapper">
|
||||
@@ -174,8 +236,8 @@
|
||||
<div id="status-message-modal" class="status-message-modal"></div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<button type="submit" id="submit-btn-modal" class="btn-submit">
|
||||
<i class="bi bi-cloud-arrow-up-fill"></i> Upload Video
|
||||
<button type="submit" id="submit-btn-modal" class="action-btn action-btn-primary btn-submit">
|
||||
<i class="bi bi-cloud-arrow-up-fill"></i> <span>Upload Video</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -324,13 +386,7 @@
|
||||
}
|
||||
|
||||
.dropzone-modal input[type="file"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropzone-icon {
|
||||
@@ -437,6 +493,7 @@
|
||||
.btn-remove-file {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
background: #333;
|
||||
border: none;
|
||||
@@ -569,28 +626,16 @@
|
||||
/* Submit Button */
|
||||
.btn-submit {
|
||||
width: 100%;
|
||||
background: #e61e1e;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 14px 24px;
|
||||
border-radius: 8px;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
background: #cc1a1a;
|
||||
}
|
||||
|
||||
.btn-submit:disabled {
|
||||
background: #555;
|
||||
background: #555 !important;
|
||||
border-color: #555 !important;
|
||||
cursor: not-allowed;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@@ -673,6 +718,15 @@ function resetUploadForm() {
|
||||
item.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('#download-options-modal .option-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
item.querySelector('input').checked = false;
|
||||
if (item.dataset.download === 'disabled') {
|
||||
item.classList.add('active');
|
||||
item.querySelector('input').checked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Format file size
|
||||
@@ -693,7 +747,8 @@ videoInputModal.addEventListener('change', function() {
|
||||
});
|
||||
|
||||
videoDropzoneModal.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('btn-remove-file')) return;
|
||||
if (e.target.closest('.btn-remove-file')) return;
|
||||
videoInputModal.click();
|
||||
});
|
||||
|
||||
videoDropzoneModal.addEventListener('dragover', (e) => {
|
||||
@@ -714,23 +769,56 @@ videoDropzoneModal.addEventListener('drop', (e) => {
|
||||
}
|
||||
});
|
||||
|
||||
const AUDIO_EXTENSIONS = ['mp3', 'm4a', 'aac', 'flac', 'wav'];
|
||||
|
||||
function isAudioFile(file) {
|
||||
const ext = file.name.split('.').pop().toLowerCase();
|
||||
return AUDIO_EXTENSIONS.includes(ext) || file.type.startsWith('audio/');
|
||||
}
|
||||
|
||||
function setAudioMode(audio) {
|
||||
// Label
|
||||
document.getElementById('file-label-icon').className = audio ? 'bi bi-music-note-beamed' : 'bi bi-camera-video';
|
||||
document.getElementById('file-label-text').textContent = audio ? 'Music File *' : 'Video or Music File *';
|
||||
|
||||
// Toggle thumbnail vs slides section
|
||||
document.getElementById('thumbnail-section-modal').style.display = audio ? 'none' : '';
|
||||
document.getElementById('slides-section-modal').style.display = audio ? '' : 'none';
|
||||
document.getElementById('slides-dropzone-modal').style.borderColor = audio ? '#e61e1e' : '';
|
||||
|
||||
// Auto-select Music type when audio
|
||||
if (audio) {
|
||||
document.querySelectorAll('#type-options-modal .option-item').forEach(o => o.classList.remove('active'));
|
||||
const musicOption = document.querySelector('#type-options-modal .option-item[data-type="music"]');
|
||||
if (musicOption) {
|
||||
musicOption.classList.add('active');
|
||||
musicOption.querySelector('input').checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update file preview icon
|
||||
const previewIcon = document.querySelector('#file-info-modal .file-preview i');
|
||||
if (previewIcon) previewIcon.className = audio ? 'bi bi-music-note-beamed' : 'bi bi-film';
|
||||
|
||||
// Update submit label
|
||||
document.getElementById('submit-btn-modal').innerHTML =
|
||||
'<i class="bi bi-cloud-arrow-up-fill"></i> <span>' + (audio ? 'Upload Track' : 'Upload Video') + '</span>';
|
||||
}
|
||||
|
||||
function handleVideoSelectModal(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0];
|
||||
const maxSize = 512 * 1024 * 1024; // 512MB
|
||||
|
||||
// Validate file type
|
||||
const validTypes = ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime', 'video/x-msvideo', 'video/x-flv', 'video/x-matroska'];
|
||||
const validExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.wmv', '.flv', '.mkv'];
|
||||
const validVideoTypes = ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime', 'video/x-msvideo', 'video/x-flv', 'video/x-matroska'];
|
||||
const validVideoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.wmv', '.flv', '.mkv'];
|
||||
const ext = '.' + file.name.split('.').pop().toLowerCase();
|
||||
|
||||
let isValidType = validTypes.includes(file.type);
|
||||
let fileExtension = '.' + file.name.split('.').pop().toLowerCase();
|
||||
if (!isValidType) {
|
||||
isValidType = validExtensions.includes(fileExtension);
|
||||
}
|
||||
const audio = isAudioFile(file);
|
||||
const validVideo = validVideoTypes.includes(file.type) || validVideoExtensions.includes(ext);
|
||||
|
||||
if (!isValidType) {
|
||||
alert('Invalid video format. Please select a valid video file (MP4, MOV, AVI, WebM, OGG, WMV, FLV, MKV).');
|
||||
if (!audio && !validVideo) {
|
||||
showToast('Invalid format. Accepted: MP4, MOV, AVI, WebM, MKV, MP3, M4A, AAC, FLAC, WAV.', 'error');
|
||||
input.value = '';
|
||||
document.getElementById('dropzone-default-modal').style.display = 'block';
|
||||
document.getElementById('file-info-modal').classList.remove('active');
|
||||
@@ -738,14 +826,16 @@ function handleVideoSelectModal(input) {
|
||||
}
|
||||
|
||||
if (file.size > maxSize) {
|
||||
alert('File size exceeds 512MB limit. Please select a smaller video file.');
|
||||
showToast('File size exceeds 512MB limit.', 'error');
|
||||
input.value = '';
|
||||
document.getElementById('dropzone-default-modal').style.display = 'block';
|
||||
document.getElementById('file-info-modal').classList.remove('active');
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto-fill title from filename (like create page)
|
||||
setAudioMode(audio);
|
||||
|
||||
// Auto-fill title from filename
|
||||
const fileNameWithoutExt = file.name.replace(/\.[^/.]+$/, '').replace(/[-_]/g, ' ');
|
||||
document.getElementById('upload-video-title').value = fileNameWithoutExt;
|
||||
|
||||
@@ -763,14 +853,26 @@ function removeVideoModal(e) {
|
||||
document.getElementById('dropzone-default-modal').style.display = 'block';
|
||||
document.getElementById('file-info-modal').classList.remove('active');
|
||||
document.getElementById('upload-video-title').value = '';
|
||||
setAudioMode(false);
|
||||
}
|
||||
|
||||
// Thumbnail Dropzone
|
||||
const thumbnailDropzoneModal = document.getElementById('thumbnail-dropzone-modal');
|
||||
const thumbnailInputModal = document.getElementById('thumbnail-modal');
|
||||
let thumbnailInputModal = document.getElementById('thumbnail-modal');
|
||||
|
||||
thumbnailInputModal.addEventListener('change', function() {
|
||||
handleThumbnailSelectModal(this);
|
||||
let _thumbClickTsModal = 0;
|
||||
thumbnailInputModal.addEventListener('change', function() { _thumbClickTsModal = 0; handleThumbnailSelectModal(this); });
|
||||
|
||||
thumbnailDropzoneModal.addEventListener('click', function(e) {
|
||||
if (e.target.closest('.btn-remove-file')) return;
|
||||
const now = Date.now();
|
||||
if (now - _thumbClickTsModal < 800) return;
|
||||
_thumbClickTsModal = now;
|
||||
if (typeof window.openCropperModal_thumb_upload === 'function') {
|
||||
window.openCropperModal_thumb_upload();
|
||||
} else {
|
||||
thumbnailInputModal.click();
|
||||
}
|
||||
});
|
||||
|
||||
thumbnailDropzoneModal.addEventListener('dragover', (e) => {
|
||||
@@ -786,8 +888,14 @@ thumbnailDropzoneModal.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
thumbnailDropzoneModal.classList.remove('dragover');
|
||||
if (e.dataTransfer.files.length) {
|
||||
thumbnailInputModal.files = e.dataTransfer.files;
|
||||
handleThumbnailSelectModal(thumbnailInputModal);
|
||||
const droppedFile = e.dataTransfer.files[0];
|
||||
if (typeof window.tcPreload_thumb_upload === 'function') {
|
||||
window.tcPreload_thumb_upload(droppedFile);
|
||||
window.openCropperModal_thumb_upload();
|
||||
} else {
|
||||
thumbnailInputModal.files = e.dataTransfer.files;
|
||||
handleThumbnailSelectModal(thumbnailInputModal);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -812,11 +920,69 @@ function handleThumbnailSelectModal(input) {
|
||||
function removeThumbnailModal(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
thumbnailInputModal.value = '';
|
||||
const fresh = thumbnailInputModal.cloneNode(false);
|
||||
thumbnailInputModal.parentNode.replaceChild(fresh, thumbnailInputModal);
|
||||
thumbnailInputModal = fresh;
|
||||
thumbnailInputModal.addEventListener('change', function() { handleThumbnailSelectModal(this); });
|
||||
document.getElementById('thumbnail-default-modal').style.display = 'block';
|
||||
document.getElementById('thumbnail-info-modal').classList.remove('active');
|
||||
}
|
||||
|
||||
// Slides dropzone (audio mode)
|
||||
const slidesDropzoneModal = document.getElementById('slides-dropzone-modal');
|
||||
let slidesInputModal = document.getElementById('slides-modal');
|
||||
|
||||
function handleSlidesSelectModal(input) {
|
||||
if (!input.files || !input.files.length) return;
|
||||
const files = Array.from(input.files);
|
||||
const strip = document.getElementById('slides-strip-modal');
|
||||
strip.innerHTML = '';
|
||||
files.forEach(f => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
const img = document.createElement('img');
|
||||
img.src = e.target.result;
|
||||
img.style.cssText = 'width:56px;height:56px;object-fit:cover;border-radius:6px;border:1px solid rgba(255,255,255,.15);';
|
||||
strip.appendChild(img);
|
||||
};
|
||||
reader.readAsDataURL(f);
|
||||
});
|
||||
const count = files.length;
|
||||
document.getElementById('slides-count-modal').textContent =
|
||||
count === 1 ? '1 image — will show as static cover' : count + ' images — will crossfade during playback';
|
||||
document.getElementById('slides-default-modal').style.display = 'none';
|
||||
document.getElementById('slides-preview-modal').style.display = '';
|
||||
slidesDropzoneModal.style.borderColor = '#22c55e';
|
||||
}
|
||||
|
||||
slidesDropzoneModal.addEventListener('click', function(e) {
|
||||
if (e.target.closest('button')) return;
|
||||
slidesInputModal.click();
|
||||
});
|
||||
slidesInputModal.addEventListener('change', function() { handleSlidesSelectModal(this); });
|
||||
slidesDropzoneModal.addEventListener('dragover', e => { e.preventDefault(); slidesDropzoneModal.classList.add('dragover'); });
|
||||
slidesDropzoneModal.addEventListener('dragleave', () => slidesDropzoneModal.classList.remove('dragover'));
|
||||
slidesDropzoneModal.addEventListener('drop', e => {
|
||||
e.preventDefault();
|
||||
slidesDropzoneModal.classList.remove('dragover');
|
||||
if (e.dataTransfer.files.length) {
|
||||
slidesInputModal.files = e.dataTransfer.files;
|
||||
handleSlidesSelectModal(slidesInputModal);
|
||||
}
|
||||
});
|
||||
|
||||
function removeSlidesModal(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const fresh = slidesInputModal.cloneNode(false);
|
||||
slidesInputModal.parentNode.replaceChild(fresh, slidesInputModal);
|
||||
slidesInputModal = fresh;
|
||||
slidesInputModal.addEventListener('change', function() { handleSlidesSelectModal(this); });
|
||||
document.getElementById('slides-default-modal').style.display = 'block';
|
||||
document.getElementById('slides-preview-modal').style.display = 'none';
|
||||
slidesDropzoneModal.style.borderColor = '#e61e1e';
|
||||
}
|
||||
|
||||
// Type options
|
||||
document.querySelectorAll('#type-options-modal .option-item').forEach(item => {
|
||||
item.querySelector('input').addEventListener('change', function() {
|
||||
@@ -833,6 +999,14 @@ document.querySelectorAll('#visibility-options-modal .option-item').forEach(item
|
||||
});
|
||||
});
|
||||
|
||||
// Download options
|
||||
document.querySelectorAll('#download-options-modal .option-item').forEach(item => {
|
||||
item.querySelector('input').addEventListener('change', function() {
|
||||
document.querySelectorAll('#download-options-modal .option-item').forEach(o => o.classList.remove('active'));
|
||||
item.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// Form submission
|
||||
document.getElementById('upload-form-modal').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
@@ -847,10 +1021,19 @@ document.getElementById('upload-form-modal').addEventListener('submit', function
|
||||
// Validate title
|
||||
const title = document.getElementById('upload-video-title').value.trim();
|
||||
if (!title) {
|
||||
showErrorModal('Please enter a title for your video');
|
||||
showErrorModal('Please enter a title');
|
||||
return;
|
||||
}
|
||||
|
||||
// At least one slide image is required for audio uploads
|
||||
if (isAudioFile(videoInput.files[0])) {
|
||||
if (!slidesInputModal.files || !slidesInputModal.files.length) {
|
||||
showErrorModal('At least one cover image is required for audio tracks');
|
||||
document.getElementById('slides-dropzone-modal').style.borderColor = '#e61e1e';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const formData = new FormData(this);
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
@@ -895,9 +1078,17 @@ document.getElementById('upload-form-modal').addEventListener('submit', function
|
||||
} else {
|
||||
try {
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
showErrorModal(response.message || `Server error (${xhr.status})`);
|
||||
// Laravel validation errors come back as { errors: { field: ['msg'] } }
|
||||
if (response.errors) {
|
||||
const msgs = Object.entries(response.errors)
|
||||
.map(([field, errs]) => `<b>${field}:</b> ${errs.join(', ')}`)
|
||||
.join('<br>');
|
||||
showErrorModal(msgs);
|
||||
} else {
|
||||
showErrorModal(response.message || `Server error (${xhr.status})`);
|
||||
}
|
||||
} catch(e) {
|
||||
showErrorModal(`Upload failed with status ${xhr.status}`);
|
||||
showErrorModal(`Upload failed (${xhr.status}): ${xhr.responseText.slice(0, 300)}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -927,3 +1118,14 @@ function showErrorModal(message) {
|
||||
document.getElementById('submit-btn-modal').innerHTML = '<i class="bi bi-cloud-arrow-up-fill"></i> Upload Video';
|
||||
}
|
||||
</script>
|
||||
|
||||
<x-image-cropper
|
||||
id="thumb_upload"
|
||||
:width="448"
|
||||
:height="252"
|
||||
shape="square"
|
||||
target-input="thumbnail-modal"
|
||||
preview-img="thumbnail-preview-img"
|
||||
output-width="1280"
|
||||
title="Crop Thumbnail"
|
||||
/>
|
||||
Reference in new issue
Block a user