WIP: storage-fix-local-nas work before playlist controls feature
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
6b3ab5b65e
commit
c160242dbc
46 files changed
+5365
-815
No files matched your search
@@ -179,7 +179,7 @@
|
||||
<div class="form-group">
|
||||
<label class="form-label"><i class="bi bi-image"></i> Current Thumbnail</label>
|
||||
<div id="current-thumb-wrap" class="{{ $video->thumbnail ? 'has-thumb' : '' }}">
|
||||
<img src="{{ $video->thumbnail ? asset('storage/thumbnails/'.$video->thumbnail) : '' }}" alt="Thumbnail">
|
||||
<img src="{{ $video->thumbnail ? route('media.thumbnail', $video->thumbnail) : '' }}" alt="Thumbnail">
|
||||
<div id="current-thumb-placeholder">
|
||||
<i class="bi bi-card-image"></i>
|
||||
<span>No thumbnail set</span>
|
||||
@@ -305,8 +305,41 @@
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{-- Replace File --}}
|
||||
<div class="danger-zone" style="margin-top:20px;">
|
||||
<p style="color:#f87171;"><i class="bi bi-arrow-repeat" style="margin-right:6px;"></i>Replace Media File</p>
|
||||
<p style="font-size:12px;color:#888;margin:-8px 0 14px;line-height:1.5;">
|
||||
Fix a corrupted or missing file without losing any views, likes, or comments.
|
||||
</p>
|
||||
|
||||
<div id="ep-rfl-dropzone" onclick="document.getElementById('ep-rfl-input').click()"
|
||||
style="border:2px dashed #444;border-radius:10px;padding:18px;text-align:center;cursor:pointer;transition:border-color .15s,background .15s;margin-bottom:10px;"
|
||||
onmouseenter="this.style.borderColor='#ef4444';this.style.background='rgba(239,68,68,.05)'"
|
||||
onmouseleave="this.style.borderColor='#444';this.style.background='transparent'">
|
||||
<i class="bi bi-cloud-upload" style="font-size:26px;color:#666;display:block;margin-bottom:6px;"></i>
|
||||
<span id="ep-rfl-label" style="font-size:13px;color:#888;">Tap to choose replacement file</span>
|
||||
<input type="file" id="ep-rfl-input" accept="video/*,audio/*,.mp4,.webm,.ogg,.mov,.avi,.wmv,.flv,.mkv,.mp3,.m4a,.aac,.wav,.flac,.opus" style="display:none;" onchange="epRflSelected(this)">
|
||||
</div>
|
||||
|
||||
<div id="ep-rfl-info" style="display:none;background:#1a1a1a;border:1px solid #333;border-radius:8px;padding:10px 14px;align-items:center;gap:10px;margin-bottom:10px;">
|
||||
<i class="bi bi-file-earmark-play" style="font-size:20px;color:#ef4444;flex-shrink:0;"></i>
|
||||
<div style="flex:1;min-width:0;">
|
||||
<div id="ep-rfl-name" style="font-size:13px;font-weight:600;color:#e5e5e5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"></div>
|
||||
<div id="ep-rfl-size" style="font-size:11px;color:#888;margin-top:2px;"></div>
|
||||
</div>
|
||||
<button type="button" onclick="epRflClear()" style="background:none;border:none;color:#888;cursor:pointer;font-size:16px;flex-shrink:0;padding:0;"><i class="bi bi-x-lg"></i></button>
|
||||
</div>
|
||||
|
||||
<div id="ep-rfl-status" style="display:none;margin-bottom:10px;font-size:12px;padding:8px 12px;border-radius:6px;"></div>
|
||||
|
||||
<button type="button" id="ep-rfl-btn" onclick="epRflSubmit()" disabled
|
||||
class="action-btn action-btn-danger" style="width:100%;justify-content:center;">
|
||||
<i class="bi bi-arrow-repeat"></i> <span>Replace File</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- Danger zone --}}
|
||||
<div class="danger-zone">
|
||||
<div class="danger-zone" style="margin-top:12px;">
|
||||
<p>Danger Zone</p>
|
||||
<form action="{{ route('videos.destroy', $video) }}" method="POST" id="delete-edit-form">
|
||||
@csrf
|
||||
@@ -380,7 +413,7 @@
|
||||
}
|
||||
@else
|
||||
// ── Slides manager (audio tracks only) ────────────────────────
|
||||
let epSlidesData = @json($video->slides->map(fn($s) => ['id' => $s->id, 'url' => asset('storage/thumbnails/'.$s->filename)])->values());
|
||||
let epSlidesData = @json($video->slides->map(fn($s) => ['id' => $s->id, 'url' => route('media.thumbnail', $s->filename)])->values());
|
||||
let epDragSrc = null;
|
||||
|
||||
function _epSyncOrder() {
|
||||
@@ -536,6 +569,75 @@
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="bi bi-check-lg"></i> <span>Save Changes</span>';
|
||||
}
|
||||
|
||||
// ── Replace File ──────────────────────────────────────────────────────
|
||||
let _epRflFile = null;
|
||||
const _epRflFmtSize = b => b >= 1073741824 ? (b/1073741824).toFixed(1)+' GB' : b >= 1048576 ? (b/1048576).toFixed(1)+' MB' : Math.round(b/1024)+' KB';
|
||||
|
||||
function epRflSelected(input) {
|
||||
_epRflFile = input.files[0] || null;
|
||||
const info = document.getElementById('ep-rfl-info');
|
||||
if (_epRflFile) {
|
||||
document.getElementById('ep-rfl-name').textContent = _epRflFile.name;
|
||||
document.getElementById('ep-rfl-size').textContent = _epRflFmtSize(_epRflFile.size);
|
||||
document.getElementById('ep-rfl-label').textContent = _epRflFile.name;
|
||||
info.style.display = 'flex';
|
||||
} else {
|
||||
info.style.display = 'none';
|
||||
document.getElementById('ep-rfl-label').textContent = 'Tap to choose replacement file';
|
||||
}
|
||||
document.getElementById('ep-rfl-btn').disabled = !_epRflFile;
|
||||
document.getElementById('ep-rfl-status').style.display = 'none';
|
||||
}
|
||||
|
||||
function epRflClear() {
|
||||
_epRflFile = null;
|
||||
document.getElementById('ep-rfl-input').value = '';
|
||||
document.getElementById('ep-rfl-info').style.display = 'none';
|
||||
document.getElementById('ep-rfl-label').textContent = 'Tap to choose replacement file';
|
||||
document.getElementById('ep-rfl-btn').disabled = true;
|
||||
document.getElementById('ep-rfl-status').style.display = 'none';
|
||||
}
|
||||
|
||||
function epRflSubmit() {
|
||||
if (!_epRflFile) return;
|
||||
const btn = document.getElementById('ep-rfl-btn');
|
||||
const status = document.getElementById('ep-rfl-status');
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Uploading…';
|
||||
status.style.display = 'none';
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append('_token', '{{ csrf_token() }}');
|
||||
fd.append('replacement_file', _epRflFile);
|
||||
|
||||
fetch('{{ route("videos.replaceFile", $video) }}', {
|
||||
method: 'POST',
|
||||
headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
|
||||
body: fd
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
status.style.cssText = 'display:block;background:rgba(74,222,128,.1);border:1px solid rgba(74,222,128,.25);color:#4ade80;';
|
||||
status.innerHTML = '<i class="bi bi-check-circle-fill"></i> ' + data.message;
|
||||
epRflClear();
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
status.style.cssText = 'display:block;background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);color:#f87171;';
|
||||
status.textContent = data.message || 'Upload failed. Please try again.';
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="bi bi-arrow-repeat"></i> <span>Replace File</span>';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
status.style.cssText = 'display:block;background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);color:#f87171;';
|
||||
status.textContent = 'Upload failed. Please try again.';
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="bi bi-arrow-repeat"></i> <span>Replace File</span>';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<x-image-cropper
|
||||
id="thumb_edit_mobile"
|
||||
|
||||
@@ -14,28 +14,8 @@
|
||||
@media (max-width: 576px) { .yt-video-grid { grid-template-columns: 1fr; gap: 14px; } }
|
||||
|
||||
|
||||
/* ── Playlist count badge (right strip on thumbnail) ── */
|
||||
.pl-count-badge {
|
||||
position: absolute; inset: 0 0 0 auto;
|
||||
width: 72px;
|
||||
background: rgba(0,0,0,.78);
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
gap: 4px; font-size: 12px; font-weight: 600; color: #fff;
|
||||
pointer-events: none;
|
||||
}
|
||||
.pl-count-badge i { font-size: 20px; }
|
||||
/* ── Thumbnail orientation fix ── */
|
||||
|
||||
/* ── Playlist type badge (top-left on thumbnail) ── */
|
||||
.feed-pl-type-badge {
|
||||
position: absolute; top: 8px; left: 8px;
|
||||
background: rgba(0,0,0,.75); color: #fff;
|
||||
font-size: 11px; font-weight: 700;
|
||||
padding: 3px 8px; border-radius: 4px;
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
text-transform: uppercase; letter-spacing: .4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── Thumbnail orientation fix ── */
|
||||
.yt-video-card .yt-video-thumb img { object-fit: cover; }
|
||||
@@ -167,7 +147,7 @@
|
||||
@include('components.video-card', ['video' => $entry['item']])
|
||||
@else
|
||||
@php $pl = $entry['item']; @endphp
|
||||
@include('components.playlist-card', ['playlist' => $pl, 'showTypeBadge' => true])
|
||||
@include('components.playlist-card', ['playlist' => $pl])
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@php
|
||||
$audioUrl = route('videos.stream', $video);
|
||||
$coverUrl = $video->thumbnail ? asset('storage/thumbnails/' . $video->thumbnail) : asset('storage/images/logo.png');
|
||||
$coverUrl = $video->thumbnail ? route('media.thumbnail', $video->thumbnail) : asset('storage/images/logo.png');
|
||||
$nextUrl = isset($nextVideo, $playlist) ? route('videos.show', $nextVideo).'?playlist='.$playlist->share_token : null;
|
||||
$prevUrl = isset($previousVideo, $playlist) ? route('videos.show', $previousVideo).'?playlist='.$playlist->share_token : null;
|
||||
$slideUrls = $video->slides->count() > 1
|
||||
? $video->slides->map(fn($s) => asset('storage/thumbnails/' . $s->filename))->values()->all()
|
||||
? $video->slides->map(fn($s) => route('media.thumbnail', $s->filename))->values()->all()
|
||||
: [];
|
||||
@endphp
|
||||
|
||||
|
||||
@@ -478,11 +478,15 @@
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- Edit Button - Only for video owner -->
|
||||
<!-- Edit / Delete Buttons - Only for video owner -->
|
||||
@if (Auth::id() === $video->user_id)
|
||||
<button class="yt-action-btn" onclick="openEditVideoModal('{{ $video->getRouteKey() }}')">
|
||||
<i class="bi bi-pencil"></i> Edit
|
||||
</button>
|
||||
<button class="yt-action-btn" onclick="openVideoDeleteDialog()"
|
||||
style="color:#ef4444;">
|
||||
<i class="bi bi-trash"></i> Delete
|
||||
</button>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="yt-action-btn">
|
||||
@@ -696,7 +700,7 @@
|
||||
style="{{ $isCurrent ? 'cursor:default;' : 'cursor:pointer;' }}">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($playlistVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $playlistVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $playlistVideo->thumbnail) }}"
|
||||
alt="{{ $playlistVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $playlistVideo->id }}/320/180"
|
||||
@@ -751,7 +755,7 @@
|
||||
onclick="window.location.href='{{ route('videos.show', $recVideo) }}'">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($recVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $recVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $recVideo->thumbnail) }}"
|
||||
alt="{{ $recVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $recVideo->id }}/320/180"
|
||||
@@ -822,7 +826,12 @@
|
||||
</button>
|
||||
|
||||
@auth
|
||||
@if (Auth::id() !== $video->user_id)
|
||||
@if (Auth::id() === $video->user_id)
|
||||
<button class="yt-action-btn" onclick="openVideoDeleteDialog()"
|
||||
style="flex:1;color:#ef4444;">
|
||||
<i class="bi bi-trash"></i><span>Delete</span>
|
||||
</button>
|
||||
@else
|
||||
<button class="yt-action-btn" style="flex:1;background:var(--brand-red);color:white;">
|
||||
<i class="bi bi-bell"></i><span>Subscribe</span>
|
||||
</button>
|
||||
@@ -886,6 +895,113 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@auth
|
||||
@if(Auth::id() === $video->user_id)
|
||||
@php $owner2fa = Auth::user()->two_factor_enabled && Auth::user()->two_factor_secret; @endphp
|
||||
|
||||
{{-- Delete confirmation dialog --}}
|
||||
<div id="videoDeleteDialog" style="display:none;position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.7);backdrop-filter:blur(4px);align-items:center;justify-content:center;padding:16px;" onclick="if(event.target===this)closeVideoDeleteDialog()">
|
||||
<div style="background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px;width:100%;max-width:420px;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.6);">
|
||||
<div style="padding:20px 24px 16px;border-bottom:1px solid var(--border-color);display:flex;align-items:center;justify-content:space-between;">
|
||||
<div style="display:flex;align-items:center;gap:10px;font-size:16px;font-weight:600;color:#ef4444;">
|
||||
<i class="bi bi-trash"></i> Delete Video
|
||||
</div>
|
||||
<button onclick="closeVideoDeleteDialog()" style="background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:18px;line-height:1;padding:4px;"><i class="bi bi-x-lg"></i></button>
|
||||
</div>
|
||||
<div style="padding:20px 24px;">
|
||||
<p style="margin:0 0 12px;font-size:14px;color:var(--text-primary);">
|
||||
You are about to permanently delete <strong>{{ $video->title }}</strong>.
|
||||
</p>
|
||||
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);border-radius:8px;padding:10px 14px;font-size:13px;color:#fca5a5;display:flex;gap:8px;align-items:flex-start;">
|
||||
<i class="bi bi-exclamation-triangle-fill" style="flex-shrink:0;margin-top:1px;"></i>
|
||||
<span>All views, likes, comments, and HLS files will also be deleted. This cannot be undone.</span>
|
||||
</div>
|
||||
@if($owner2fa)
|
||||
<div style="margin-top:16px;">
|
||||
<label style="font-size:13px;color:var(--text-secondary);display:flex;align-items:center;gap:6px;margin-bottom:8px;">
|
||||
<i class="bi bi-shield-lock-fill" style="color:#a78bfa;"></i>
|
||||
Enter your <strong style="color:var(--text-primary);">2FA code</strong> to confirm
|
||||
</label>
|
||||
<input type="text" id="delVideoOtp" inputmode="numeric" pattern="[0-9]*"
|
||||
maxlength="6" autocomplete="one-time-code" placeholder="000000"
|
||||
style="width:100%;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:10px 14px;color:#fff;font-size:22px;letter-spacing:.3em;text-align:center;box-sizing:border-box;">
|
||||
</div>
|
||||
@endif
|
||||
<div id="delVideoError" style="display:none;margin-top:12px;padding:8px 12px;background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.3);border-radius:6px;color:#f87171;font-size:13px;"></div>
|
||||
</div>
|
||||
<div style="padding:12px 24px 20px;display:flex;gap:10px;justify-content:flex-end;">
|
||||
<button onclick="closeVideoDeleteDialog()" class="yt-action-btn">Cancel</button>
|
||||
<button id="delVideoConfirmBtn" onclick="confirmVideoDelete()" class="yt-action-btn" style="background:#ef4444;color:#fff;border-color:#ef4444;">
|
||||
<i class="bi bi-trash"></i> Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const _owner2fa = {{ $owner2fa ? 'true' : 'false' }};
|
||||
const _videoDeleteUrl = '{{ route('videos.destroy', $video) }}';
|
||||
const _videoCsrf = '{{ csrf_token() }}';
|
||||
|
||||
function openVideoDeleteDialog() {
|
||||
const dlg = document.getElementById('videoDeleteDialog');
|
||||
dlg.style.display = 'flex';
|
||||
dlg.style.alignItems = 'center';
|
||||
dlg.style.justifyContent = 'center';
|
||||
document.getElementById('delVideoError').style.display = 'none';
|
||||
if (_owner2fa) { document.getElementById('delVideoOtp').value = ''; setTimeout(() => document.getElementById('delVideoOtp').focus(), 100); }
|
||||
}
|
||||
function closeVideoDeleteDialog() {
|
||||
document.getElementById('videoDeleteDialog').style.display = 'none';
|
||||
}
|
||||
function confirmVideoDelete() {
|
||||
const btn = document.getElementById('delVideoConfirmBtn');
|
||||
const errEl = document.getElementById('delVideoError');
|
||||
const otpCode = _owner2fa ? document.getElementById('delVideoOtp').value.replace(/\s/g,'') : '';
|
||||
|
||||
if (_owner2fa && otpCode.length !== 6) {
|
||||
errEl.textContent = 'Please enter your 6-digit 2FA code.';
|
||||
errEl.style.display = 'block';
|
||||
document.getElementById('delVideoOtp').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="bi bi-hourglass-split"></i> Deleting…';
|
||||
errEl.style.display = 'none';
|
||||
|
||||
const body = new URLSearchParams({ '_token': _videoCsrf, '_method': 'DELETE' });
|
||||
if (_owner2fa) body.append('otp_code', otpCode);
|
||||
|
||||
fetch(_videoDeleteUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: body.toString()
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
errEl.textContent = data.message || 'Delete failed.';
|
||||
errEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="bi bi-trash"></i> Delete';
|
||||
if (_owner2fa) { document.getElementById('delVideoOtp').value = ''; document.getElementById('delVideoOtp').focus(); }
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
errEl.textContent = 'An error occurred. Please try again.';
|
||||
errEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="bi bi-trash"></i> Delete';
|
||||
});
|
||||
}
|
||||
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeVideoDeleteDialog(); });
|
||||
</script>
|
||||
@endif
|
||||
@endauth
|
||||
@endsection
|
||||
|
||||
|
||||
|
||||
@@ -455,7 +455,7 @@
|
||||
onclick="window.location.href='{{ route('videos.show', $playlistVideo) }}?playlist={{ $playlist->share_token }}'">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($playlistVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $playlistVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $playlistVideo->thumbnail) }}"
|
||||
alt="{{ $playlistVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $playlistVideo->id }}/320/180"
|
||||
@@ -507,7 +507,7 @@
|
||||
onclick="window.location.href='{{ route('videos.show', $recVideo) }}'">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($recVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $recVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $recVideo->thumbnail) }}"
|
||||
alt="{{ $recVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $recVideo->id }}/320/180"
|
||||
|
||||
@@ -2543,7 +2543,7 @@
|
||||
onclick="window.location.href='{{ route('videos.show', $playlistVideo) }}?playlist={{ $playlist->share_token }}'">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($playlistVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $playlistVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $playlistVideo->thumbnail) }}"
|
||||
alt="{{ $playlistVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $playlistVideo->id }}/320/180"
|
||||
@@ -2595,7 +2595,7 @@
|
||||
onclick="window.location.href='{{ route('videos.show', $recVideo) }}'">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($recVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $recVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $recVideo->thumbnail) }}"
|
||||
alt="{{ $recVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $recVideo->id }}/320/180"
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
onclick="window.location.href='{{ route('videos.show', $playlistVideo) }}?playlist={{ $playlist->share_token }}'">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($playlistVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $playlistVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $playlistVideo->thumbnail) }}"
|
||||
alt="{{ $playlistVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $playlistVideo->id }}/320/180"
|
||||
@@ -535,7 +535,7 @@
|
||||
onclick="window.location.href='{{ route('videos.show', $recVideo) }}'">
|
||||
<div class="sidebar-thumb" style="position: relative;">
|
||||
@if ($recVideo->thumbnail)
|
||||
<img src="{{ asset('storage/thumbnails/' . $recVideo->thumbnail) }}"
|
||||
<img src="{{ route('media.thumbnail', $recVideo->thumbnail) }}"
|
||||
alt="{{ $recVideo->title }}">
|
||||
@else
|
||||
<img src="https://picsum.photos/seed/{{ $recVideo->id }}/320/180"
|
||||
|
||||
Reference in new issue
Block a user