Folder View MP4
This commit is contained in:
1 parent
5593d5e392
commit
9496824637
2 files changed
+55
-7
No files matched your search
+13
-3
@@ -104,18 +104,28 @@ def api_recount_progress():
|
|||||||
|
|
||||||
@app.route("/api/mp4-files")
|
@app.route("/api/mp4-files")
|
||||||
def api_mp4_files():
|
def api_mp4_files():
|
||||||
files = []
|
folders = {}
|
||||||
output = Path(OUTPUT_DIR)
|
output = Path(OUTPUT_DIR)
|
||||||
if output.exists():
|
if output.exists():
|
||||||
for f in sorted(output.rglob("*.mp4"), key=lambda p: p.stat().st_mtime, reverse=True):
|
for f in sorted(output.rglob("*.mp4"), key=lambda p: p.stat().st_mtime, reverse=True):
|
||||||
st = f.stat()
|
st = f.stat()
|
||||||
files.append({
|
rel = f.relative_to(output)
|
||||||
|
folder = str(rel.parent) if str(rel.parent) != "." else ""
|
||||||
|
if folder not in folders:
|
||||||
|
folders[folder] = []
|
||||||
|
folders[folder].append({
|
||||||
"name": f.name,
|
"name": f.name,
|
||||||
"path": str(f),
|
"path": str(f),
|
||||||
"size": st.st_size,
|
"size": st.st_size,
|
||||||
"mtime": datetime.fromtimestamp(st.st_mtime).isoformat(),
|
"mtime": datetime.fromtimestamp(st.st_mtime).isoformat(),
|
||||||
})
|
})
|
||||||
return jsonify(files)
|
result = []
|
||||||
|
for folder, files in sorted(folders.items(), key=lambda x: x[0], reverse=True):
|
||||||
|
result.append({
|
||||||
|
"folder": folder,
|
||||||
|
"files": files,
|
||||||
|
})
|
||||||
|
return jsonify(result)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/start-recount", methods=["POST"])
|
@app.route("/api/start-recount", methods=["POST"])
|
||||||
|
|||||||
@@ -288,6 +288,23 @@
|
|||||||
}
|
}
|
||||||
.file-item.streaming .fi-icon { color: var(--accent4); animation: blink 1.5s ease-in-out infinite; }
|
.file-item.streaming .fi-icon { color: var(--accent4); animation: blink 1.5s ease-in-out infinite; }
|
||||||
|
|
||||||
|
.folder-header {
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
padding: 8px 10px; margin-bottom: 2px;
|
||||||
|
background: var(--cell-bg);
|
||||||
|
border: 1px solid var(--cell-border); border-radius: 8px;
|
||||||
|
cursor: pointer; transition: border-color 0.2s;
|
||||||
|
font-size: 10px; letter-spacing: 1px;
|
||||||
|
color: var(--accent2);
|
||||||
|
}
|
||||||
|
.folder-header:hover { border-color: var(--border-glow); }
|
||||||
|
.folder-header .folder-arrow {
|
||||||
|
font-size: 10px; transition: transform 0.2s; width: 14px; text-align: center;
|
||||||
|
}
|
||||||
|
.folder-header.open .folder-arrow { transform: rotate(90deg); }
|
||||||
|
.folder-body { display: none; padding-left: 8px; }
|
||||||
|
.folder-body.open { display: block; }
|
||||||
|
|
||||||
/* Buttons */
|
/* Buttons */
|
||||||
.btn {
|
.btn {
|
||||||
font-family: 'Orbitron', sans-serif; font-size: 10px; letter-spacing: 2px;
|
font-family: 'Orbitron', sans-serif; font-size: 10px; letter-spacing: 2px;
|
||||||
@@ -493,30 +510,51 @@ function applyState(data) {
|
|||||||
async function loadFiles() {
|
async function loadFiles() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/mp4-files');
|
const res = await fetch('/api/mp4-files');
|
||||||
const files = await res.json();
|
const folders = await res.json();
|
||||||
const container = document.getElementById('file-list');
|
const container = document.getElementById('file-list');
|
||||||
if (files.length === 0) {
|
if (!folders || folders.length === 0) {
|
||||||
container.innerHTML = '<div style="font-size:10px;color:var(--text-secondary);text-align:center;padding:20px;">No MP4 files found</div>';
|
container.innerHTML = '<div style="font-size:10px;color:var(--text-secondary);text-align:center;padding:20px;">No MP4 files found</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
container.innerHTML = files.map(f => {
|
container.innerHTML = folders.map((g, gi) => {
|
||||||
|
const label = g.folder || 'root';
|
||||||
|
const fid = 'folder-' + gi;
|
||||||
|
const wasOpen = document.getElementById(fid) && document.getElementById(fid).classList.contains('open');
|
||||||
|
return `
|
||||||
|
<div class="folder-header" id="${fid}-hdr" onclick="toggleFolder('${fid}')">
|
||||||
|
<span class="folder-arrow">▶</span>
|
||||||
|
<i class="fas fa-folder" style="color:var(--accent2);"></i>
|
||||||
|
<span>${label}</span>
|
||||||
|
<span style="font-size:8px;color:var(--text-secondary);">(${g.files.length})</span>
|
||||||
|
</div>
|
||||||
|
<div class="folder-body${wasOpen ? ' open' : ''}" id="${fid}">
|
||||||
|
${g.files.map(f => {
|
||||||
const sel = selectedFile === f.path;
|
const sel = selectedFile === f.path;
|
||||||
const str = streaming && selectedFile === f.path;
|
const str = streaming && selectedFile === f.path;
|
||||||
let cls = 'file-item';
|
let cls = 'file-item';
|
||||||
if (str) cls += ' streaming';
|
if (str) cls += ' streaming';
|
||||||
else if (sel) cls += ' selected';
|
else if (sel) cls += ' selected';
|
||||||
return `
|
return `
|
||||||
<div class="${cls}" onclick="selectFile('${f.path.replace(/'/g, "\\'")}', '${f.name.replace(/'/g, "\\'")}')">
|
<div class="${cls}" onclick="event.stopPropagation(); selectFile('${f.path.replace(/'/g, "\\'")}', '${f.name.replace(/'/g, "\\'")}')">
|
||||||
<span class="fi-icon">${str ? '◉' : '▶'}</span>
|
<span class="fi-icon">${str ? '◉' : '▶'}</span>
|
||||||
<span class="fi-name">${f.name}</span>
|
<span class="fi-name">${f.name}</span>
|
||||||
<span class="fi-size">${formatSize(f.size)}</span>
|
<span class="fi-size">${formatSize(f.size)}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
}).join('')}
|
||||||
|
</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load files:', err);
|
console.error('Failed to load files:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleFolder(fid) {
|
||||||
|
const hdr = document.getElementById(fid + '-hdr');
|
||||||
|
const body = document.getElementById(fid);
|
||||||
|
if (hdr) hdr.classList.toggle('open');
|
||||||
|
if (body) body.classList.toggle('open');
|
||||||
|
}
|
||||||
|
|
||||||
function selectFile(path, name) {
|
function selectFile(path, name) {
|
||||||
if (selectedFile === path) {
|
if (selectedFile === path) {
|
||||||
selectedFile = null;
|
selectedFile = null;
|
||||||
|
|||||||
Reference in new issue
Block a user