修改自适应
This commit is contained in:
@@ -26,25 +26,83 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
videoListEl.classList.remove('hidden');
|
||||
|
||||
if (data.videos.length === 0) {
|
||||
videoListEl.innerHTML = '<p style="color: var(--text-secondary); text-align: center; padding: 2rem;">No MP4 videos found in the S3 bucket.</p>';
|
||||
videoListEl.innerHTML = '<p style="color: var(--text-secondary); text-align: center; padding: 2rem;">No videos found.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a tree structure from S3 keys
|
||||
const tree = {};
|
||||
data.videos.forEach(key => {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'video-item';
|
||||
li.innerHTML = `
|
||||
<div class="video-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 8-6 4 6 4V8Z"/><rect width="14" height="12" x="2" y="6" rx="2" ry="2"/></svg>
|
||||
</div>
|
||||
<div class="video-info">
|
||||
<div class="video-title" title="${key}">${key.split('/').pop()}</div>
|
||||
<div class="video-meta">H264 / AAC</div>
|
||||
</div>
|
||||
`;
|
||||
li.addEventListener('click', () => selectVideo(key, li));
|
||||
videoListEl.appendChild(li);
|
||||
const parts = key.split('/');
|
||||
let current = tree;
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
if (!current[part]) {
|
||||
current[part] = (i === parts.length - 1) ? key : {};
|
||||
}
|
||||
if (i < parts.length - 1) {
|
||||
current = current[part];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Recursive function to render the tree
|
||||
const renderTree = (node, container) => {
|
||||
for (const [name, value] of Object.entries(node)) {
|
||||
if (typeof value === 'string') {
|
||||
// It's a file
|
||||
const li = document.createElement('li');
|
||||
li.className = 'video-item file-item';
|
||||
const ext = name.split('.').pop().toUpperCase();
|
||||
li.innerHTML = `
|
||||
<div class="video-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 8-6 4 6 4V8Z"/><rect width="14" height="12" x="2" y="6" rx="2" ry="2"/></svg>
|
||||
</div>
|
||||
<div class="video-info">
|
||||
<div class="video-title" title="${value}">${name}</div>
|
||||
<div class="video-meta">Video / ${ext}</div>
|
||||
</div>
|
||||
`;
|
||||
li.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
selectVideo(value, li);
|
||||
});
|
||||
container.appendChild(li);
|
||||
} else {
|
||||
// It's a folder
|
||||
const li = document.createElement('li');
|
||||
li.className = 'folder-item';
|
||||
|
||||
const folderHeader = document.createElement('div');
|
||||
folderHeader.className = 'folder-header';
|
||||
folderHeader.innerHTML = `
|
||||
<div class="folder-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
||||
</div>
|
||||
<div class="folder-title" title="${name}">${name}</div>
|
||||
<div class="folder-toggle">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="chevron"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const subListContainer = document.createElement('ul');
|
||||
subListContainer.className = 'sub-list hidden';
|
||||
|
||||
folderHeader.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
li.classList.toggle('open');
|
||||
subListContainer.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
li.appendChild(folderHeader);
|
||||
renderTree(value, subListContainer);
|
||||
li.appendChild(subListContainer);
|
||||
container.appendChild(li);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderTree(tree, videoListEl);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
loadingSpinner.innerHTML = `<p style="color: #ef4444;">Error: ${err.message}</p>`;
|
||||
|
||||
@@ -40,10 +40,15 @@ app.get('/api/videos', async (req, res) => {
|
||||
|
||||
const response = await s3Client.send(command);
|
||||
|
||||
// Filter out non-mp4 files for this boilerplate
|
||||
// Filter for common video formats
|
||||
const videoExtensions = ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv', '.m4v'];
|
||||
const videos = (response.Contents || [])
|
||||
.map(item => item.Key)
|
||||
.filter(key => key && key.toLowerCase().endsWith('.mp4'));
|
||||
.filter(key => {
|
||||
if (!key) return false;
|
||||
const lowerKey = key.toLowerCase();
|
||||
return videoExtensions.some(ext => lowerKey.endsWith(ext));
|
||||
});
|
||||
|
||||
res.json({ videos });
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user