latest update my Video Platform is working Great
This commit is contained in:
parent
eb707c1ee1
commit
dcdcafe0ba
44
TODO.md
44
TODO.md
@ -1,33 +1,27 @@
|
||||
# TODO: Add Delete and Edit options to channel video 3-dot menu
|
||||
# TODO: Two-Stage Upload Modal Implementation
|
||||
|
||||
## Task
|
||||
In `https://video.innovator.bh/channel/2`, the 3-dot menu must have Delete and Edit options.
|
||||
Restructure the video upload modal to have:
|
||||
- Stage 1: Upload video file (with progress bar)
|
||||
- Stage 2: Title and Description
|
||||
- Stage 3: Thumbnail
|
||||
- Stage 4: Privacy settings
|
||||
|
||||
## Requirements
|
||||
- `/videos` (public area) - NO edit/delete options
|
||||
- `/channel/{id}` (user channel page) - Edit and Delete options for the channel owner only
|
||||
## Implementation Steps
|
||||
|
||||
## Plan
|
||||
### Step 1: Update VideoController.php ✅
|
||||
- [x] Add new endpoint for temp video upload (returns temp filename)
|
||||
- [x] Modify store method to handle two-step process
|
||||
|
||||
### Step 1: Modify app.blade.php
|
||||
- Add include for edit-video-modal partial
|
||||
### Step 2: Update routes/web.php ✅
|
||||
- [x] Add route for temp video upload
|
||||
|
||||
### Step 2: Modify channel.blade.php
|
||||
- Add conditional check for video ownership
|
||||
- Add Edit button that calls openEditVideoModal()
|
||||
- Add Delete button with confirmation
|
||||
- Add separator before owner-only options
|
||||
### Step 3: Update upload-modal.blade.php ✅
|
||||
- [x] Restructure steps: Video → Details → Thumbnail → Privacy
|
||||
- [x] Update step indicators
|
||||
- [x] Add video upload with progress bar in Stage 1
|
||||
- [x] Update JavaScript for new flow
|
||||
|
||||
### Step 3: Add JavaScript for delete functionality
|
||||
- Add deleteVideo() function with AJAX call
|
||||
### Step 4: Test
|
||||
- [ ] Test the complete upload flow
|
||||
|
||||
### Step 4: Update VideoController
|
||||
- Add ownership check to destroy method
|
||||
- Return JSON for AJAX requests
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Files Modified
|
||||
1. `resources/views/layouts/app.blade.php` - Added edit-video-modal include
|
||||
2. `resources/views/user/channel.blade.php` - Added Edit/Delete in dropdown
|
||||
3. `app/Http/Controllers/VideoController.php` - Updated destroy method
|
||||
|
||||
@ -55,7 +55,7 @@ class VideoController extends Controller
|
||||
$request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'video' => 'required|file|mimes:mp4,webm,ogg,mov,avi,wmv,flv,mkv|max:2000000',
|
||||
'video' => 'required|file|mimes:mp4,webm,ogg,mov,avi,wmv,flv,mkv|max:512000',
|
||||
'thumbnail' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:5120',
|
||||
'visibility' => 'nullable|in:public,unlisted,private',
|
||||
]);
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@yield('title', 'TAKEONE')</title>
|
||||
|
||||
@stack('head')
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<style>
|
||||
@ -288,15 +291,103 @@
|
||||
.yt-main { padding: 16px; }
|
||||
}
|
||||
|
||||
/* Mobile Search */
|
||||
.mobile-search {
|
||||
display: none;
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
/* Mobile Search Toggle Button */
|
||||
.yt-mobile-search-toggle {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.mobile-search { display: block; }
|
||||
.yt-mobile-search-toggle:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
/* Mobile Search Overlay */
|
||||
.mobile-search-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--bg-dark);
|
||||
z-index: 1001;
|
||||
padding: 60px 16px 16px;
|
||||
}
|
||||
|
||||
.mobile-search-overlay.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mobile-search-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mobile-search-close {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.mobile-search-close:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.mobile-search-form {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.mobile-search-input {
|
||||
flex: 1;
|
||||
background: #121212;
|
||||
border: 1px solid var(--border-color);
|
||||
border-right: none;
|
||||
border-radius: 20px 0 0 20px;
|
||||
padding: 0 16px;
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mobile-search-input:focus {
|
||||
outline: none;
|
||||
border-color: #1c62b9;
|
||||
}
|
||||
|
||||
.mobile-search-submit {
|
||||
width: 50px;
|
||||
background: #222;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0 20px 20px 0;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mobile-search-submit:hover {
|
||||
background: #303030;
|
||||
}
|
||||
|
||||
/* Dropdown */
|
||||
@ -321,6 +412,21 @@
|
||||
<!-- Header -->
|
||||
@include('layouts.partials.header')
|
||||
|
||||
<!-- Mobile Search Overlay -->
|
||||
<div class="mobile-search-overlay" id="mobileSearchOverlay">
|
||||
<div class="mobile-search-header">
|
||||
<button type="button" class="mobile-search-close" onclick="toggleMobileSearch()">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</button>
|
||||
<form action="{{ route('videos.search') }}" method="GET" class="mobile-search-form">
|
||||
<input type="text" name="q" class="mobile-search-input" placeholder="Search" value="{{ request('q') }}">
|
||||
<button type="submit" class="mobile-search-submit">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar Overlay (Mobile) -->
|
||||
<div class="yt-sidebar-overlay" onclick="toggleSidebar()"></div>
|
||||
|
||||
@ -329,16 +435,6 @@
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="yt-main" id="main">
|
||||
<!-- Mobile Search -->
|
||||
<div class="mobile-search">
|
||||
<div class="yt-search">
|
||||
<input type="text" class="yt-search-input" placeholder="Search">
|
||||
<button class="yt-search-btn">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
@ -350,6 +446,29 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Mobile search toggle function
|
||||
function toggleMobileSearch() {
|
||||
const overlay = document.getElementById('mobileSearchOverlay');
|
||||
overlay.classList.toggle('active');
|
||||
|
||||
// Focus input when overlay opens
|
||||
if (overlay.classList.contains('active')) {
|
||||
setTimeout(function() {
|
||||
document.querySelector('.mobile-search-input').focus();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Close mobile search on escape key
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
const overlay = document.getElementById('mobileSearchOverlay');
|
||||
if (overlay.classList.contains('active')) {
|
||||
overlay.classList.remove('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sidebar toggle function
|
||||
function toggleSidebar() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
|
||||
@ -196,7 +196,7 @@
|
||||
background: linear-gradient(145deg, #1e1e1e 0%, #252525 100%);
|
||||
border: 1px solid #3a3a3a;
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6), 0 0 40px rgba(139, 92, 246, 0.1);
|
||||
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6), 0 0 40px rgba(230, 30, 30, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@ -233,7 +233,7 @@
|
||||
|
||||
/* Header */
|
||||
.edit-modal-header {
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
||||
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
||||
border-bottom: none;
|
||||
padding: 20px 24px;
|
||||
position: relative;
|
||||
@ -326,10 +326,10 @@
|
||||
}
|
||||
|
||||
.edit-step.active .step-circle {
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
||||
border-color: #8b5cf6;
|
||||
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
||||
border-color: #e61e1e;
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.4);
|
||||
box-shadow: 0 4px 15px rgba(230, 30, 30, 0.4);
|
||||
}
|
||||
|
||||
.edit-step.completed .step-circle {
|
||||
@ -353,7 +353,7 @@
|
||||
}
|
||||
|
||||
.edit-step.active .step-label {
|
||||
color: #8b5cf6;
|
||||
color: #e61e1e;
|
||||
}
|
||||
|
||||
.step-line {
|
||||
@ -366,7 +366,7 @@
|
||||
}
|
||||
|
||||
.step-line.active {
|
||||
background: linear-gradient(90deg, #8b5cf6, #22c55e);
|
||||
background: linear-gradient(90deg, #e61e1e, #22c55e);
|
||||
}
|
||||
|
||||
/* Step Content */
|
||||
@ -461,7 +461,7 @@
|
||||
}
|
||||
|
||||
.form-label i {
|
||||
color: #8b5cf6;
|
||||
color: #e61e1e;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@ -478,8 +478,8 @@
|
||||
|
||||
.form-input:focus, .form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: #8b5cf6;
|
||||
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
|
||||
border-color: #e61e1e;
|
||||
box-shadow: 0 0 0 3px rgba(230, 30, 30, 0.15);
|
||||
}
|
||||
|
||||
.form-input::placeholder, .form-textarea::placeholder {
|
||||
@ -499,13 +499,13 @@
|
||||
}
|
||||
|
||||
.dropzone-modal:hover {
|
||||
border-color: #8b5cf6;
|
||||
background: rgba(139, 92, 246, 0.05);
|
||||
border-color: #e61e1e;
|
||||
background: rgba(230, 30, 30, 0.05);
|
||||
}
|
||||
|
||||
.dropzone-modal.dragover {
|
||||
border-color: #8b5cf6;
|
||||
background: rgba(139, 92, 246, 0.1);
|
||||
border-color: #e61e1e;
|
||||
background: rgba(230, 30, 30, 0.1);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
@ -521,7 +521,7 @@
|
||||
|
||||
.dropzone-icon {
|
||||
font-size: 48px;
|
||||
color: #8b5cf6;
|
||||
color: #e61e1e;
|
||||
margin-bottom: 12px;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
@ -562,7 +562,7 @@
|
||||
.file-preview {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
||||
background: linear-gradient(135deg, #e61e1e 0%, #ff6b6b 100%);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -615,7 +615,7 @@
|
||||
}
|
||||
|
||||
.btn-remove-file:hover {
|
||||
background: #8b5cf6;
|
||||
background: #e61e1e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@ -651,8 +651,8 @@
|
||||
}
|
||||
|
||||
.visibility-option-modal.active .visibility-content-modal {
|
||||
border-color: #8b5cf6;
|
||||
background: rgba(139, 92, 246, 0.1);
|
||||
border-color: #e61e1e;
|
||||
background: rgba(230, 30, 30, 0.1);
|
||||
}
|
||||
|
||||
.visibility-content-modal i {
|
||||
@ -664,7 +664,7 @@
|
||||
}
|
||||
|
||||
.visibility-option-modal.active .visibility-content-modal i {
|
||||
color: #8b5cf6;
|
||||
color: #e61e1e;
|
||||
}
|
||||
|
||||
.visibility-text {
|
||||
@ -719,14 +719,14 @@
|
||||
}
|
||||
|
||||
.btn-next, .btn-save-changes {
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
|
||||
background: linear-gradient(135deg, #e61e1e 0%, #ff4757 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
|
||||
box-shadow: 0 4px 15px rgba(230, 30, 30, 0.3);
|
||||
}
|
||||
|
||||
.btn-next:hover, .btn-save-changes:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4);
|
||||
box-shadow: 0 6px 20px rgba(230, 30, 30, 0.4);
|
||||
}
|
||||
|
||||
.btn-next:disabled, .btn-save-changes:disabled {
|
||||
|
||||
@ -22,6 +22,11 @@
|
||||
</div>
|
||||
|
||||
<div class="yt-header-right">
|
||||
<!-- Mobile Search Toggle Button -->
|
||||
<button type="button" class="yt-mobile-search-toggle d-md-none" onclick="toggleMobileSearch()">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
|
||||
@auth
|
||||
<button type="button" class="yt-upload-btn" onclick="openUploadModal()">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
<span>1</span>
|
||||
<i class="bi bi-check-lg"></i>
|
||||
</div>
|
||||
<span class="step-label">Details</span>
|
||||
<span class="step-label">Video</span>
|
||||
</div>
|
||||
<div class="step-line"></div>
|
||||
<div class="upload-step" data-step="2">
|
||||
@ -33,7 +33,7 @@
|
||||
<span>2</span>
|
||||
<i class="bi bi-check-lg"></i>
|
||||
</div>
|
||||
<span class="step-label">Video</span>
|
||||
<span class="step-label">Details</span>
|
||||
</div>
|
||||
<div class="step-line"></div>
|
||||
<div class="upload-step" data-step="3">
|
||||
@ -57,36 +57,8 @@
|
||||
<form id="upload-form-modal" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<!-- Step 1: Details -->
|
||||
<!-- Step 1: Video File -->
|
||||
<div class="upload-step-content active" data-step="1">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-card-heading"></i> Title *
|
||||
</label>
|
||||
<input type="text" name="title" required
|
||||
class="form-input"
|
||||
placeholder="Give your video a catchy title">
|
||||
</div>
|
||||
|
||||
<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"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="step-navigation">
|
||||
<div></div>
|
||||
<button type="button" class="btn-next" onclick="nextStep(2)">
|
||||
Next Step <i class="bi bi-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Video File -->
|
||||
<div class="upload-step-content" data-step="2">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-camera-video"></i> Video File *
|
||||
@ -98,7 +70,7 @@
|
||||
<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 up to 2GB</p>
|
||||
<p class="dropzone-hint">MP4, MOV, AVI, WebM up to 512MB</p>
|
||||
</div>
|
||||
<div id="file-info-modal" class="file-info-modal">
|
||||
<div class="file-preview">
|
||||
@ -115,11 +87,39 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-navigation">
|
||||
<div></div>
|
||||
<button type="button" class="btn-next" onclick="nextStep(2)" id="btn-step-1" disabled>
|
||||
Next Step <i class="bi bi-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Details -->
|
||||
<div class="upload-step-content" data-step="2">
|
||||
<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="Give your video a catchy title">
|
||||
</div>
|
||||
|
||||
<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"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="step-navigation">
|
||||
<button type="button" class="btn-prev" onclick="prevStep(1)">
|
||||
<i class="bi bi-arrow-left"></i> Back
|
||||
</button>
|
||||
<button type="button" class="btn-next" onclick="nextStep(3)" id="btn-step-2" disabled>
|
||||
<button type="button" class="btn-next" onclick="nextStep(3)">
|
||||
Next Step <i class="bi bi-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -891,13 +891,24 @@ function resetUploadForm() {
|
||||
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';
|
||||
document.getElementById('btn-step-1').disabled = true;
|
||||
}
|
||||
|
||||
// Step Navigation
|
||||
function nextStep(step) {
|
||||
// Validate current step before moving
|
||||
if (step === 2) {
|
||||
const title = document.querySelector('input[name="title"]').value.trim();
|
||||
// Step 1 -> Step 2: require video
|
||||
const videoInput = document.getElementById('video-modal');
|
||||
if (!videoInput.files || !videoInput.files[0]) {
|
||||
alert('Please select a video file first');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (step === 3) {
|
||||
// Step 2 -> Step 3: require title
|
||||
const title = document.getElementById('upload-video-title').value.trim();
|
||||
if (!title) {
|
||||
alert('Please enter a title for your video');
|
||||
return;
|
||||
@ -977,13 +988,43 @@ videoDropzoneModal.addEventListener('drop', (e) => {
|
||||
function handleVideoSelectModal(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0];
|
||||
const maxSize = 512 * 1024 * 1024; // 512MB in bytes
|
||||
|
||||
// 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');
|
||||
document.getElementById('btn-step-1').disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > maxSize) {
|
||||
alert('File size exceeds 512MB limit. Please select a smaller video file.');
|
||||
input.value = ''; // Clear the input
|
||||
document.getElementById('dropzone-default-modal').style.display = 'block';
|
||||
document.getElementById('file-info-modal').classList.remove('active');
|
||||
document.getElementById('btn-step-1').disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('filename-modal').textContent = file.name;
|
||||
document.getElementById('filesize-modal').textContent = (file.size / 1024 / 1024).toFixed(2) + ' MB';
|
||||
document.getElementById('dropzone-default-modal').style.display = 'none';
|
||||
document.getElementById('file-info-modal').classList.add('active');
|
||||
|
||||
// Enable next button
|
||||
document.getElementById('btn-step-2').disabled = false;
|
||||
document.getElementById('btn-step-1').disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -993,7 +1034,7 @@ function removeVideoModal(e) {
|
||||
videoInputModal.value = '';
|
||||
document.getElementById('dropzone-default-modal').style.display = 'block';
|
||||
document.getElementById('file-info-modal').classList.remove('active');
|
||||
document.getElementById('btn-step-2').disabled = true;
|
||||
document.getElementById('btn-step-1').disabled = true;
|
||||
}
|
||||
|
||||
// Thumbnail Dropzone
|
||||
|
||||
@ -276,7 +276,7 @@
|
||||
<div id="dropzone-default">
|
||||
<i class="bi bi-camera-video icon"></i>
|
||||
<p>Click to select or drag video here</p>
|
||||
<p class="hint">MP4, MOV, AVI, WebM up to 2GB</p>
|
||||
<p class="hint">MP4, MOV, AVI, WebM up to 512MB</p>
|
||||
</div>
|
||||
<div id="file-info">
|
||||
<p class="filename" id="filename"></p>
|
||||
@ -384,6 +384,34 @@
|
||||
function handleFileSelect(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0];
|
||||
const maxSize = 512 * 1024 * 1024; // 512MB in bytes
|
||||
|
||||
// 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').style.display = 'block';
|
||||
document.getElementById('file-info').classList.remove('active');
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > maxSize) {
|
||||
alert('File size exceeds 512MB limit. Please select a smaller video file.');
|
||||
input.value = ''; // Clear the input
|
||||
document.getElementById('dropzone-default').style.display = 'block';
|
||||
document.getElementById('file-info').classList.remove('active');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('filename').textContent = file.name;
|
||||
document.getElementById('filesize').textContent = (file.size / 1024 / 1024).toFixed(2) + ' MB';
|
||||
document.getElementById('dropzone-default').style.display = 'none';
|
||||
|
||||
@ -331,7 +331,7 @@ function playVideo(card) {
|
||||
const video = card.querySelector('video');
|
||||
if (video) {
|
||||
video.currentTime = 0;
|
||||
video.volume = 0.25; // Set volume to 25%
|
||||
video.volume = 0.10; // Set volume to 10%
|
||||
video.play().catch(function(e) {
|
||||
// Auto-play may be blocked, ignore error
|
||||
});
|
||||
@ -347,6 +347,27 @@ function stopVideo(card) {
|
||||
video.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile touch support for hover-like behavior
|
||||
document.addEventListener('touchstart', function(e) {
|
||||
const card = e.target.closest('.yt-video-card');
|
||||
if (card) {
|
||||
// Stop any other playing videos first
|
||||
document.querySelectorAll('.yt-video-card').forEach(function(otherCard) {
|
||||
if (otherCard !== card) {
|
||||
stopVideo(otherCard);
|
||||
}
|
||||
});
|
||||
playVideo(card);
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchend', function(e) {
|
||||
const card = e.target.closest('.yt-video-card');
|
||||
if (card) {
|
||||
stopVideo(card);
|
||||
}
|
||||
}, { passive: true });
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
|
||||
@ -1,5 +1,21 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@push('head')
|
||||
<!-- Open Graph / WhatsApp / Facebook / Twitter Preview -->
|
||||
<meta property="og:title" content="{{ $video->title }}">
|
||||
<meta property="og:description" content="{{ $video->description ? Str::limit($video->description, 200) : 'Check out this video on TAKEONE' }}">
|
||||
<meta property="og:image" content="{{ $video->thumbnail_url }}">
|
||||
<meta property="og:url" content="{{ $video->share_url }}">
|
||||
<meta property="og:type" content="video.other">
|
||||
<meta property="og:site_name" content="TAKEONE">
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="{{ $video->title }}">
|
||||
<meta name="twitter:description" content="{{ $video->description ? Str::limit($video->description, 200) : 'Check out this video on TAKEONE' }}">
|
||||
<meta name="twitter:image" content="{{ $video->thumbnail_url }}">
|
||||
@endpush
|
||||
|
||||
@section('title', $video->title . ' | TAKEONE')
|
||||
|
||||
@section('extra_styles')
|
||||
@ -18,11 +34,19 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.video-container.portrait { aspect-ratio: 9/16; }
|
||||
.video-container.square { aspect-ratio: 1/1; }
|
||||
.video-container.ultrawide { aspect-ratio: 21/9; }
|
||||
.video-container.portrait,
|
||||
.video-container.square,
|
||||
.video-container.ultrawide {
|
||||
margin: 0 auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.video-container.portrait { aspect-ratio: 9/16; max-width: 50vh; }
|
||||
.video-container.square { aspect-ratio: 1/1; max-width: 70vh; }
|
||||
.video-container.ultrawide { aspect-ratio: 21/9; max-width: 100%; }
|
||||
|
||||
.video-container video { width: 100%; height: 100%; object-fit: contain; }
|
||||
|
||||
@ -171,24 +195,64 @@
|
||||
.yt-header-center { display: none; }
|
||||
.sidebar-video-card { flex-direction: column; }
|
||||
.sidebar-thumb { width: 100%; }
|
||||
|
||||
/* Video Layout Container - Stack vertically on tablet/mobile */
|
||||
.video-layout-container {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
.yt-video-section {
|
||||
width: 100% !important;
|
||||
flex: none !important;
|
||||
}
|
||||
.yt-sidebar-container {
|
||||
width: 100% !important;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.video-stats-row { flex-direction: column; align-items: flex-start; }
|
||||
.video-actions { width: 100%; overflow-x: auto; justify-content: flex-start; }
|
||||
.yt-main { padding: 16px; }
|
||||
.yt-main { padding: 12px !important; }
|
||||
|
||||
/* Mobile video player fixes */
|
||||
.video-container {
|
||||
max-height: 50vh !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.video-container video {
|
||||
object-fit: contain !important;
|
||||
}
|
||||
.video-title {
|
||||
font-size: 16px !important;
|
||||
margin: 12px 0 6px !important;
|
||||
}
|
||||
.channel-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start !important;
|
||||
gap: 12px;
|
||||
}
|
||||
.channel-info {
|
||||
width: 100%;
|
||||
}
|
||||
.subscribe-btn {
|
||||
width: 100%;
|
||||
}
|
||||
.video-description {
|
||||
padding: 12px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<!-- Video Layout Container -->
|
||||
<div style="display: flex; gap: 24px; max-width: 1800px; margin: 0 auto;">
|
||||
<div class="video-layout-container" style="display: flex; gap: 24px; max-width: 1800px; margin: 0 auto;">
|
||||
<!-- Video Section -->
|
||||
<div class="yt-video-section">
|
||||
<!-- Video Player -->
|
||||
<div class="video-container @if($video->orientation === 'portrait') portrait @elseif($video->orientation === 'square') square @elseif($video->orientation === 'ultrawide') ultrawide @endif" id="videoContainer">
|
||||
<video id="videoPlayer" controls playsinline preload="metadata">
|
||||
<video id="videoPlayer" controls playsinline preload="metadata" autoplay>
|
||||
<source src="{{ route('videos.stream', $video->id) }}" type="video/mp4">
|
||||
</video>
|
||||
</div>
|
||||
@ -285,20 +349,19 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var videoPlayer = document.getElementById('videoPlayer');
|
||||
if (videoPlayer) {
|
||||
// Try to autoplay with sound (works because user just clicked on the video)
|
||||
// Set volume to 50%
|
||||
videoPlayer.volume = 0.5;
|
||||
|
||||
// Try to autoplay with sound
|
||||
var playPromise = videoPlayer.play();
|
||||
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.then(function() {
|
||||
// Autoplay started successfully
|
||||
console.log('Video autoplayed with sound');
|
||||
console.log('Video autoplayed with sound at 50% volume');
|
||||
}).catch(function(error) {
|
||||
// Autoplay was prevented, try muted
|
||||
console.log('Autoplay blocked, attempting muted autoplay');
|
||||
videoPlayer.muted = true;
|
||||
videoPlayer.play().catch(function(e) {
|
||||
console.log('Muted autoplay also blocked');
|
||||
});
|
||||
// Autoplay was prevented
|
||||
console.log('Autoplay blocked');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user