refactor(admin): show media files from GCS bucket
This commit is contained in:
parent
e8be7ec3b1
commit
08da62553c
1 changed files with 91 additions and 48 deletions
|
|
@ -92,41 +92,53 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 1. Use modal-xl for desktop real estate -->
|
||||
<div class="modal fade" id="photoModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalUserName">User Details</h5>
|
||||
<h5 class="modal-title">Record Details</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="" id="modalImage" class="user-photo me-3" style="width: 80px; height: 80px;">
|
||||
<div>
|
||||
<h4 id="modalUserName" class="mb-0"></h4>
|
||||
<small id="modalSession" class="text-muted"></small>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<!-- Left Column: Media (Fixed Width for 9:16) -->
|
||||
<div class="col-md-4 border-end">
|
||||
<h6>Original Photo</h6>
|
||||
<img src="" id="modalImage" class="img-thumbnail mb-3"
|
||||
style="width: 150px; height: 150px; object-fit: cover;">
|
||||
|
||||
<h6>Generated Media</h6>
|
||||
<div id="modalMediaContainer" class="bg-black rounded overflow-hidden shadow-sm"
|
||||
style="max-width: 280px; min-height: 200px;">
|
||||
<!-- Video/Audio injected here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Full Data Table -->
|
||||
<div class="col-md-8">
|
||||
<h6>Full Entry Metadata</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover border">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="modalDetailsTableBody">
|
||||
<!-- Dynamic content -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h6 class="mt-4">Lyrics</h6>
|
||||
|
||||
<pre id="modalLyrics" class="bg-dark text-info p-3 rounded"
|
||||
style="white-space: pre-wrap; font-size: 0.85rem; max-height: 300px; overflow-y: auto;">
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6>Pet Details</h6>
|
||||
<table class="table table-sm border">
|
||||
<tr><td>Pet:</td><td id="modalPetName" class=""></td></tr>
|
||||
<tr><td>Type:</td><td id="modalPetType"></td></tr>
|
||||
<tr><td>Vibe:</td><td id="modalVibe"></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6>Assets</h6>
|
||||
<div id="modalMediaContainer">
|
||||
<!-- We will inject an <audio> or <video> tag here via JS -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12 mt-3">
|
||||
<h6>Lyrics</h6>
|
||||
<pre id="modalLyrics" class="bg-dark text-white p-3 rounded small" style="max-height: 150px; overflow-y: auto;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -146,11 +158,14 @@
|
|||
$(document).ready(function() {
|
||||
const API_BASE_URL = '';
|
||||
const API_ENDPOINT = `/back/api/admin/data`;
|
||||
const MEDIA_BASE_URL = 'https://storage.googleapis.com/vday2026/';
|
||||
|
||||
// Helper to convert container paths to web URLs
|
||||
function fixStoragePath(path) {
|
||||
if (!path) return '';
|
||||
return path.replace(/^\/app\/storage\//, '/storage/');
|
||||
// let relative_path = path.replace(/^\/app\/storage\//, '/storage/');
|
||||
const isRelative = path.startsWith('uploads/') || path.startsWith('audio/') || path.startsWith('video/');
|
||||
return isRelative ? MEDIA_BASE_URL + path : path;
|
||||
}
|
||||
|
||||
// Fetch and display Sonauto credits
|
||||
|
|
@ -274,39 +289,67 @@
|
|||
const rowData = table.row($(this).closest('tr')).data();
|
||||
|
||||
// 2. Populate the Modal fields
|
||||
$('#modalUserName').text(rowData.owner_name + ' - Profile');
|
||||
$('.modal-title').text(`Session: ${rowData.session_id}`);
|
||||
$('#modalImage').attr('src', fixStoragePath(rowData.photo_path) || "https://placehold.co/500x500/fbba0e/e11d48/jpg?text=No\nImage");
|
||||
$('#modalPetName').text(rowData.pet_name || 'N/A');
|
||||
$('#modalPetType').text(rowData.pet_type || 'N/A');
|
||||
$('#modalVibe').text(rowData.music_vibe || 'N/A');
|
||||
$('#modalSession').text(rowData.session_id);
|
||||
// 3. Handle Lyrics
|
||||
if (rowData.lyrics) {
|
||||
$('#modalLyrics').text(rowData.lyrics);
|
||||
} else {
|
||||
$('#modalLyrics').text('No lyrics available yet...');
|
||||
}
|
||||
|
||||
// 3. Dynamic Table Generation for all 20+ columns
|
||||
const tableBody = $('#modalDetailsTableBody');
|
||||
tableBody.empty();
|
||||
// List of keys we want to display or skip
|
||||
Object.entries(rowData).forEach(([key, value]) => {
|
||||
// Skip keys that are handled visually (media/lyrics)
|
||||
if (['photo_path', 'generated_video_path', 'generated_song_path', 'lyrics'].includes(key))
|
||||
return;
|
||||
|
||||
let displayValue = value ?? '<span class="text-muted italic">null</span>';
|
||||
// Surgical Link Detection: Only link if it has a known file extension
|
||||
const isFile = typeof value === 'string' && /\.(jpg|jpeg|png|mp3|mp4|wav)$/i.test(value);
|
||||
if (isFile) {
|
||||
const fullUrl = fixStoragePath(value);
|
||||
displayValue = `<a href="${fullUrl}" target="_blank" class="text-decoration-none text-truncate d-inline-block" style="max-width: 300px;">${value} 🔗</a>`;
|
||||
}
|
||||
tableBody.append(`
|
||||
<tr>
|
||||
<td class="fw-bold text-muted" style="width: 30%">${key.replace(/_/g, ' ')}</td>
|
||||
<td class="text-break">${displayValue}</td>
|
||||
</tr>
|
||||
`);
|
||||
});
|
||||
|
||||
$('#modalLyrics').text(rowData.lyrics || 'No lyrics generated.');
|
||||
|
||||
|
||||
// 4. Handle Media (Song or Video)
|
||||
const mediaContainer = $('#modalMediaContainer');
|
||||
mediaContainer.empty(); // Clear previous content
|
||||
|
||||
console.group(`--- Media Debug: Session ${rowData.session_id} ---`);
|
||||
const videoUrl = fixStoragePath(rowData.generated_video_path);
|
||||
const audioUrl = fixStoragePath(rowData.generated_song_path);
|
||||
console.log('Video URL:', videoUrl);
|
||||
console.log('Audio URL:', audioUrl);
|
||||
console.groupEnd();
|
||||
|
||||
// Reset container state
|
||||
mediaContainer.empty().removeClass('ratio ratio-9x16 d-flex align-items-center p-3');
|
||||
|
||||
if (rowData.generated_video_path) {
|
||||
if (videoUrl && rowData.generated_video_path) {
|
||||
console.log('Injecting Video...');
|
||||
mediaContainer.addClass('ratio ratio-9x16');
|
||||
mediaContainer.html(`
|
||||
<video controls class="w-100" style="max-height: 300px;">
|
||||
<source src="${fixStoragePath(rowData.generated_video_path)}" type="video/mp4">
|
||||
Your browser does not support video.
|
||||
<video controls preload="metadata" class="w-100 h-100" src="${videoUrl}">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
`);
|
||||
} else if (rowData.generated_song_path) {
|
||||
} else if (audioUrl && rowData.generated_song_path) {
|
||||
console.log('Injecting Audio...');
|
||||
mediaContainer.addClass('d-flex align-items-center p-3');
|
||||
mediaContainer.html(`
|
||||
<audio controls class="w-100">
|
||||
<source src="${fixStoragePath(rowData.generated_song_path)}" type="audio/mpeg">
|
||||
<audio controls preload="metadata" class="w-100" src="${audioUrl}">
|
||||
Your browser does not support audio.
|
||||
</audio>
|
||||
`);
|
||||
} else {
|
||||
mediaContainer.html('<p class="text-muted">No media available yet...</p>');
|
||||
mediaContainer.html('<div class="d-flex align-items-center justify-content-center h-100 text-white-50">No Media</div>');
|
||||
}
|
||||
|
||||
// 5. Show the Modal
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue