2026-03-11 11:21:33 +03:00

930 lines
29 KiB
PHP

<!-- Upload Modal - Single Page Form (like create.blade.php) -->
<div class="modal fade" id="uploadModal" tabindex="-1" aria-labelledby="uploadModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content upload-modal-content">
<!-- Header -->
<div class="modal-header upload-modal-header">
<div class="d-flex align-items-center gap-3">
<div class="upload-icon-wrapper">
<i class="bi bi-cloud-arrow-up-fill"></i>
</div>
<div>
<h5 class="modal-title" id="uploadModalLabel">Upload Video</h5>
<span class="upload-subtitle">Share your creativity</span>
</div>
</div>
<button type="button" class="btn-close btn-close-white" onclick="closeUploadModal()" aria-label="Close"></button>
</div>
<!-- Body -->
<div class="modal-body upload-modal-body">
<!-- Form - Single Page (no steps) -->
<form id="upload-form-modal" enctype="multipart/form-data">
@csrf
<!-- Video File -->
<div class="form-group">
<label class="form-label">
<i class="bi bi-camera-video"></i> Video File *
</label>
<div id="video-dropzone-modal" class="dropzone-modal">
<input type="file" name="video" id="video-modal" accept="video/*" 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>
</div>
<div id="file-info-modal" class="file-info-modal">
<div class="file-preview">
<i class="bi bi-film"></i>
</div>
<div class="file-details">
<p class="filename" id="filename-modal"></p>
<p id="filesize-modal"></p>
</div>
<button type="button" class="btn-remove-file" onclick="removeVideoModal(event)">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
</div>
<!-- Title -->
<div class="form-group">
<label class="form-label">
<i class="bi bi-card-heading"></i> Title *
</label>
<input type="text" name="title" id="upload-video-title" required
class="form-input"
placeholder="Enter video title">
</div>
<!-- Description -->
<div class="form-group">
<label class="form-label">
<i class="bi bi-text-paragraph"></i> Description
</label>
<textarea name="description" rows="3"
class="form-textarea"
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
</label>
<div id="thumbnail-dropzone-modal" class="dropzone-modal thumbnail-dropzone">
<input type="file" name="thumbnail" id="thumbnail-modal" accept="image/*">
<div id="thumbnail-default-modal">
<div class="dropzone-icon thumbnail-icon">
<i class="bi bi-card-image"></i>
</div>
<p class="dropzone-title">Click to select thumbnail (optional)</p>
</div>
<div id="thumbnail-info-modal" class="file-info-modal thumbnail-info">
<div class="file-preview thumbnail-preview">
<img id="thumbnail-preview-img" src="" alt="Thumbnail preview">
</div>
<div class="file-details">
<p class="filename" id="thumbnail-filename-modal"></p>
<p id="thumbnail-filesize-modal"></p>
</div>
<button type="button" class="btn-remove-file" onclick="removeThumbnailModal(event)">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
</div>
<!-- Video Type -->
<div class="form-group">
<label class="form-label">
<i class="bi bi-collection-play"></i> Video Type
</label>
<div class="options-grid" id="type-options-modal">
<label class="option-item active" data-type="generic">
<input type="radio" name="type" value="generic" checked>
<div class="option-content">
<i class="bi bi-film"></i>
<span>Generic</span>
</div>
</label>
<label class="option-item" data-type="music">
<input type="radio" name="type" value="music">
<div class="option-content">
<i class="bi bi-music-note"></i>
<span>Music</span>
</div>
</label>
<label class="option-item" data-type="match">
<input type="radio" name="type" value="match">
<div class="option-content">
<i class="bi bi-trophy"></i>
<span>Match</span>
</div>
</label>
</div>
</div>
<!-- Privacy -->
<div class="form-group">
<label class="form-label">
<i class="bi bi-shield-lock"></i> Privacy
</label>
<div class="options-grid" id="visibility-options-modal">
<label class="option-item active" data-privacy="public">
<input type="radio" name="visibility" value="public" checked>
<div class="option-content">
<i class="bi bi-globe"></i>
<span>Public</span>
</div>
</label>
<label class="option-item" data-privacy="unlisted">
<input type="radio" name="visibility" value="unlisted">
<div class="option-content">
<i class="bi bi-link-45deg"></i>
<span>Unlisted</span>
</div>
</label>
<label class="option-item" data-privacy="private">
<input type="radio" name="visibility" value="private">
<div class="option-content">
<i class="bi bi-lock"></i>
<span>Private</span>
</div>
</label>
</div>
</div>
<!-- Hidden Shorts -->
<input type="hidden" name="is_shorts" id="is_shorts_modal" value="0">
<!-- Progress Bar -->
<div id="progress-container-modal" class="progress-container-modal">
<div class="progress-bar-wrapper">
<div id="progress-bar-modal" class="progress-bar-fill"></div>
</div>
<p id="progress-text-modal" class="progress-text">Uploading... 0%</p>
</div>
<!-- Status Message -->
<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>
</form>
</div>
</div>
</div>
</div>
<style>
/* Modal Base Styles */
.upload-modal-content {
background: #1a1a1a;
border: none;
border-radius: 12px;
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6);
overflow: hidden;
}
/* Modal Animation */
#uploadModal .modal-dialog {
max-width: 640px;
transform: scale(0.9) translateY(20px);
opacity: 0;
transition: all 0.3s ease;
}
#uploadModal.show .modal-dialog {
transform: scale(1) translateY(0);
opacity: 1;
}
/* Header */
.upload-modal-header {
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
border-bottom: none;
padding: 20px 24px;
position: relative;
overflow: hidden;
}
.upload-modal-header::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 100%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
pointer-events: none;
}
.upload-modal-header .btn-close {
position: relative;
z-index: 1;
}
.upload-icon-wrapper {
width: 48px;
height: 48px;
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: white;
backdrop-filter: blur(10px);
}
.upload-modal-header .modal-title {
font-size: 20px;
font-weight: 600;
color: white;
margin: 0;
}
.upload-subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
}
/* Body */
.upload-modal-body {
padding: 24px;
background: #1a1a1a;
}
/* Form Elements */
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
font-size: 14px;
color: #e5e5e5;
}
.form-label i {
color: #e61e1e;
margin-right: 6px;
}
.form-input, .form-textarea {
width: 100%;
background: #121212;
border: 1px solid #333;
border-radius: 8px;
padding: 12px 16px;
color: #f1f1f1;
font-size: 14px;
transition: all 0.2s;
}
.form-input:focus, .form-textarea:focus {
outline: none;
border-color: #e61e1e;
box-shadow: 0 0 0 3px rgba(230, 30, 30, 0.15);
}
.form-input::placeholder, .form-textarea::placeholder {
color: #666;
}
/* Dropzone */
.dropzone-modal {
border: 2px dashed #404040;
border-radius: 12px;
padding: 40px 20px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
position: relative;
background: #151515;
}
.dropzone-modal:hover {
border-color: #e61e1e;
background: rgba(230, 30, 30, 0.05);
}
.dropzone-modal.dragover {
border-color: #e61e1e;
background: rgba(230, 30, 30, 0.1);
}
.dropzone-modal input[type="file"] {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
.dropzone-icon {
font-size: 48px;
color: #666;
margin-bottom: 12px;
}
.dropzone-icon.thumbnail-icon {
font-size: 28px;
}
.dropzone-title {
color: #aaa;
font-size: 14px;
margin: 8px 0;
}
.dropzone-hint {
color: #666;
font-size: 12px;
margin: 0;
}
/* Thumbnail Dropzone - Smaller */
.thumbnail-dropzone {
padding: 20px;
}
.thumbnail-dropzone .dropzone-icon {
font-size: 28px;
margin-bottom: 6px;
}
.thumbnail-dropzone .dropzone-title {
font-size: 13px;
margin: 0;
}
/* File Info */
.file-info-modal {
display: none;
align-items: center;
gap: 12px;
padding: 12px;
background: #1f1f1f;
border-radius: 8px;
border: 1px solid #333;
}
.file-info-modal.active {
display: flex;
}
.file-preview {
width: 48px;
height: 48px;
background: #333;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: white;
flex-shrink: 0;
}
.file-preview.thumbnail-preview {
background: none;
overflow: hidden;
width: 80px;
height: 48px;
}
.file-preview.thumbnail-preview img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 6px;
}
.file-details {
flex: 1;
text-align: left;
}
.file-details .filename {
color: #e5e5e5;
font-weight: 500;
font-size: 13px;
margin: 0 0 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
.file-details p {
color: #888;
font-size: 12px;
margin: 0;
}
.btn-remove-file {
width: 28px;
height: 28px;
border-radius: 50%;
background: #333;
border: none;
color: #888;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
flex-shrink: 0;
}
.btn-remove-file:hover {
background: #e61e1e;
color: white;
}
/* Options Grid */
.options-grid {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.option-item {
flex: 1;
min-width: 80px;
cursor: pointer;
}
.option-item input {
display: none;
}
.option-content {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px;
background: #1a1a1a;
border: 2px solid #333;
border-radius: 8px;
transition: all 0.2s;
}
.option-item:hover .option-content {
border-color: #555;
background: #1f1f1f;
}
.option-item.active .option-content {
border-color: #e61e1e;
background: rgba(230, 30, 30, 0.1);
}
.option-content i {
font-size: 16px;
color: #666;
width: 18px;
text-align: center;
}
.option-item.active .option-content i {
color: #e61e1e;
}
.option-content span {
font-size: 13px;
color: #aaa;
}
.option-item.active .option-content span {
color: #fff;
}
/* Progress Bar */
.progress-container-modal {
display: none;
margin: 20px 0;
}
.progress-container-modal.active {
display: block;
}
.progress-bar-wrapper {
background: #2a2a2a;
border-radius: 4px;
height: 4px;
overflow: hidden;
}
.progress-bar-fill {
background: #e61e1e;
height: 100%;
width: 0%;
transition: width 0.3s;
}
.progress-text {
text-align: center;
color: #888;
font-size: 13px;
margin-top: 8px;
}
/* Status Message */
.status-message-modal {
display: none;
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 16px;
font-size: 13px;
}
.status-message-modal.success {
display: block;
background: rgba(34, 197, 94, 0.2);
color: #4ade80;
border: 1px solid rgba(34, 197, 94, 0.3);
}
.status-message-modal.error {
display: block;
background: rgba(239, 68, 68, 0.2);
color: #f87171;
border: 1px solid rgba(239, 68, 68, 0.3);
}
/* Submit Button */
.btn-submit {
width: 100%;
background: #e61e1e;
color: white;
border: none;
padding: 14px 24px;
border-radius: 8px;
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;
cursor: not-allowed;
}
/* Responsive */
@media (max-width: 576px) {
.upload-modal-body {
padding: 16px;
}
.options-grid {
flex-direction: column;
}
.option-item {
min-width: 100%;
}
}
</style>
<script>
// Initialize modal functions
function openUploadModal() {
// Check if mobile device - redirect to create page on mobile
if (window.innerWidth < 992) {
window.location.href = '{{ route("videos.create") }}';
return;
}
const modalEl = document.getElementById('uploadModal');
const modal = new bootstrap.Modal(modalEl);
modal.show();
// Add show class for animation
setTimeout(() => {
modalEl.classList.add('show');
}, 10);
}
function closeUploadModal() {
const modalEl = document.getElementById('uploadModal');
const modal = bootstrap.Modal.getInstance(modalEl);
if (modal) {
modal.hide();
}
// Reset form after hidden
modalEl.addEventListener('hidden.bs.modal', function() {
resetUploadForm();
modalEl.classList.remove('show');
}, { once: true });
}
function resetUploadForm() {
document.getElementById('upload-form-modal').reset();
// Reset file previews
document.getElementById('dropzone-default-modal').style.display = 'block';
document.getElementById('file-info-modal').classList.remove('active');
document.getElementById('thumbnail-default-modal').style.display = 'block';
document.getElementById('thumbnail-info-modal').classList.remove('active');
// Reset progress
document.getElementById('progress-container-modal').classList.remove('active');
document.getElementById('progress-bar-modal').style.width = '0%';
document.getElementById('status-message-modal').className = 'status-message-modal';
document.getElementById('submit-btn-modal').disabled = false;
document.getElementById('submit-btn-modal').innerHTML = '<i class="bi bi-cloud-arrow-up-fill"></i> Upload Video';
// Reset option items
document.querySelectorAll('#type-options-modal .option-item').forEach(item => {
item.classList.remove('active');
if (item.dataset.type === 'generic') {
item.classList.add('active');
}
});
document.querySelectorAll('#visibility-options-modal .option-item').forEach(item => {
item.classList.remove('active');
if (item.dataset.privacy === 'public') {
item.classList.add('active');
}
});
}
// Format file size
function formatFileSizeModal(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Video Dropzone
const videoDropzoneModal = document.getElementById('video-dropzone-modal');
const videoInputModal = document.getElementById('video-modal');
videoInputModal.addEventListener('change', function() {
handleVideoSelectModal(this);
});
videoDropzoneModal.addEventListener('click', function(e) {
if (e.target.classList.contains('btn-remove-file')) return;
});
videoDropzoneModal.addEventListener('dragover', (e) => {
e.preventDefault();
videoDropzoneModal.classList.add('dragover');
});
videoDropzoneModal.addEventListener('dragleave', () => {
videoDropzoneModal.classList.remove('dragover');
});
videoDropzoneModal.addEventListener('drop', (e) => {
e.preventDefault();
videoDropzoneModal.classList.remove('dragover');
if (e.dataTransfer.files.length) {
videoInputModal.files = e.dataTransfer.files;
handleVideoSelectModal(videoInputModal);
}
});
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'];
let isValidType = validTypes.includes(file.type);
let fileExtension = '.' + file.name.split('.').pop().toLowerCase();
if (!isValidType) {
isValidType = validExtensions.includes(fileExtension);
}
if (!isValidType) {
alert('Invalid video format. Please select a valid video file (MP4, MOV, AVI, WebM, OGG, WMV, FLV, MKV).');
input.value = '';
document.getElementById('dropzone-default-modal').style.display = 'block';
document.getElementById('file-info-modal').classList.remove('active');
return;
}
if (file.size > maxSize) {
alert('File size exceeds 512MB limit. Please select a smaller video file.');
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)
const fileNameWithoutExt = file.name.replace(/\.[^/.]+$/, '').replace(/[-_]/g, ' ');
document.getElementById('upload-video-title').value = fileNameWithoutExt;
document.getElementById('filename-modal').textContent = file.name;
document.getElementById('filesize-modal').textContent = formatFileSizeModal(file.size);
document.getElementById('dropzone-default-modal').style.display = 'none';
document.getElementById('file-info-modal').classList.add('active');
}
}
function removeVideoModal(e) {
e.preventDefault();
e.stopPropagation();
videoInputModal.value = '';
document.getElementById('dropzone-default-modal').style.display = 'block';
document.getElementById('file-info-modal').classList.remove('active');
document.getElementById('upload-video-title').value = '';
}
// Thumbnail Dropzone
const thumbnailDropzoneModal = document.getElementById('thumbnail-dropzone-modal');
const thumbnailInputModal = document.getElementById('thumbnail-modal');
thumbnailInputModal.addEventListener('change', function() {
handleThumbnailSelectModal(this);
});
thumbnailDropzoneModal.addEventListener('dragover', (e) => {
e.preventDefault();
thumbnailDropzoneModal.classList.add('dragover');
});
thumbnailDropzoneModal.addEventListener('dragleave', () => {
thumbnailDropzoneModal.classList.remove('dragover');
});
thumbnailDropzoneModal.addEventListener('drop', (e) => {
e.preventDefault();
thumbnailDropzoneModal.classList.remove('dragover');
if (e.dataTransfer.files.length) {
thumbnailInputModal.files = e.dataTransfer.files;
handleThumbnailSelectModal(thumbnailInputModal);
}
});
function handleThumbnailSelectModal(input) {
if (input.files && input.files[0]) {
const file = input.files[0];
document.getElementById('thumbnail-filename-modal').textContent = file.name;
document.getElementById('thumbnail-filesize-modal').textContent = formatFileSizeModal(file.size);
// Show thumbnail preview
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('thumbnail-preview-img').src = e.target.result;
};
reader.readAsDataURL(file);
document.getElementById('thumbnail-default-modal').style.display = 'none';
document.getElementById('thumbnail-info-modal').classList.add('active');
}
}
function removeThumbnailModal(e) {
e.preventDefault();
e.stopPropagation();
thumbnailInputModal.value = '';
document.getElementById('thumbnail-default-modal').style.display = 'block';
document.getElementById('thumbnail-info-modal').classList.remove('active');
}
// Type options
document.querySelectorAll('#type-options-modal .option-item').forEach(item => {
item.querySelector('input').addEventListener('change', function() {
document.querySelectorAll('#type-options-modal .option-item').forEach(o => o.classList.remove('active'));
item.classList.add('active');
});
});
// Visibility options
document.querySelectorAll('#visibility-options-modal .option-item').forEach(item => {
item.querySelector('input').addEventListener('change', function() {
document.querySelectorAll('#visibility-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();
// Validate video file
const videoInput = document.getElementById('video-modal');
if (!videoInput.files || !videoInput.files[0]) {
showErrorModal('Please select a video file');
return;
}
// Validate title
const title = document.getElementById('upload-video-title').value.trim();
if (!title) {
showErrorModal('Please enter a title for your video');
return;
}
const formData = new FormData(this);
const xhr = new XMLHttpRequest();
// Set timeout for large file uploads (30 minutes)
xhr.timeout = 1800000;
// Show progress
document.getElementById('progress-container-modal').classList.add('active');
document.getElementById('submit-btn-modal').disabled = true;
document.getElementById('submit-btn-modal').innerHTML = '<i class="bi bi-arrow-repeat"></i> Uploading...';
document.getElementById('status-message-modal').className = 'status-message-modal';
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
document.getElementById('progress-bar-modal').style.width = percent + '%';
document.getElementById('progress-text-modal').textContent = 'Uploading... ' + percent + '%';
}
});
xhr.addEventListener('load', function() {
document.getElementById('progress-bar-modal').style.width = '100%';
document.getElementById('progress-text-modal').textContent = 'Processing...';
if (xhr.status >= 200 && xhr.status < 300) {
try {
const response = JSON.parse(xhr.responseText);
if (response.success) {
document.getElementById('progress-text-modal').textContent = 'Done! Redirecting...';
setTimeout(() => {
closeUploadModal();
window.location.href = response.redirect;
}, 1000);
} else {
showErrorModal(response.message || 'Upload failed');
}
} catch(e) {
showErrorModal('Invalid response from server');
}
} else if (xhr.status === 0) {
showErrorModal('Connection lost. Please check your internet connection.');
} else {
try {
const response = JSON.parse(xhr.responseText);
showErrorModal(response.message || `Server error (${xhr.status})`);
} catch(e) {
showErrorModal(`Upload failed with status ${xhr.status}`);
}
}
});
xhr.addEventListener('error', function() {
showErrorModal('Upload failed. Please check your internet connection.');
});
xhr.addEventListener('timeout', function() {
showErrorModal('Upload timed out. The file may be too large.');
});
xhr.addEventListener('abort', function() {
showErrorModal('Upload was cancelled.');
});
xhr.open('POST', '{{ route("videos.store") }}');
xhr.setRequestHeader('X-CSRF-TOKEN', '{{ csrf_token() }}');
xhr.send(formData);
});
function showErrorModal(message) {
document.getElementById('progress-container-modal').classList.remove('active');
document.getElementById('status-message-modal').innerHTML = '<i class="bi bi-exclamation-circle-fill"></i> ' + message;
document.getElementById('status-message-modal').className = 'status-message-modal error';
document.getElementById('submit-btn-modal').disabled = false;
document.getElementById('submit-btn-modal').innerHTML = '<i class="bi bi-cloud-arrow-up-fill"></i> Upload Video';
}
</script>