feat: Verbesserung der Speicherinformationen-Funktion zur besseren Unterscheidung von Quell- und Zielgeräten; Versionsnummer auf 1.0.70 erhöht

This commit is contained in:
2026-05-13 11:08:21 +02:00
parent cf2d2869d8
commit 851903eba4
2 changed files with 49 additions and 69 deletions

114
app.py
View File

@@ -1534,43 +1534,47 @@ def r_storage_info():
devs = usb_devices() devs = usb_devices()
result = [] result = []
src_ports = {sp['port'] for sp in _resolve_source_ports(cfg)}
dst_port = cfg.get('dest_port')
def _du_for_dev(dev): def _du_for_dev(dev):
mp, owned = ensure_mount(dev) mp, owned = ensure_mount(dev)
if not mp: if not mp:
return dict(mounted=False, total=None, used=None, free=None, pct=None) return dict(total=None, used=None, free=None, pct=None)
try: try:
du = shutil.disk_usage(mp) du = shutil.disk_usage(mp)
return dict(mounted=True, total=du.total, used=du.used, free=du.free, return dict(total=du.total, used=du.used, free=du.free,
pct=round(du.used / du.total * 100) if du.total else 0) pct=round(du.used / du.total * 100) if du.total else 0)
except Exception: except Exception:
return dict(mounted=False, total=None, used=None, free=None, pct=None) return dict(total=None, used=None, free=None, pct=None)
finally: finally:
if owned: if owned:
subprocess.run(['umount', mp], capture_output=True) subprocess.run(['umount', mp], capture_output=True)
for sp in _resolve_source_ports(cfg): for dev in devs:
dev = next((d for d in devs if d['usb_port'] == sp['port']), None) port = dev['usb_port']
entry = dict(type='source', label=sp.get('label') or f"Port {sp['port']}", if port in src_ports:
port=sp['port'], mounted=False, role = 'source'
total=None, used=None, free=None, pct=None) elif port == dst_port:
if dev: role = 'dest'
else:
role = 'other'
entry = dict(
role=role,
label=dev.get('label') or dev.get('device') or f'Port {port}',
port=port,
device=dev.get('device', ''),
size_str=dev.get('size', ''),
)
entry.update(_du_for_dev(dev)) entry.update(_du_for_dev(dev))
result.append(entry) result.append(entry)
if cfg.get('dest_type') == 'internal': if cfg.get('dest_type') == 'internal':
entry = dict(type='dest', entry = dict(role='dest',
label=cfg.get('internal_dest_label') or 'Interner Speicher', label=cfg.get('internal_dest_label') or 'Interner Speicher',
port='__internal__') port='__internal__', device='internal', size_str='')
entry.update(_du_for_dev({'internal': True})) entry.update(_du_for_dev({'internal': True}))
result.append(entry) result.append(entry)
elif cfg.get('dest_port'):
dev = next((d for d in devs if d['usb_port'] == cfg['dest_port']), None)
entry = dict(type='dest', label=cfg.get('dest_label') or f"Port {cfg['dest_port']}",
port=cfg['dest_port'], mounted=False,
total=None, used=None, free=None, pct=None)
if dev:
entry.update(_du_for_dev(dev))
result.append(entry)
return jsonify(result) return jsonify(result)
@@ -2503,8 +2507,7 @@ body{background:var(--bg);color:var(--txt);font-family:-apple-system,BlinkMacSys
<div class="card-head"> <div class="card-head">
<div class="card-icon green">&#8644;</div> <div class="card-icon green">&#8644;</div>
<span class="card-title">USB Ports &amp; Datei-Explorer</span> <span class="card-title">USB Ports &amp; Datei-Explorer</span>
<button class="btn sm ghost" style="margin-left:auto" onclick="toggleStoragePanel()">&#128190;&nbsp;Speicher</button> <button class="btn sm ghost danger" style="margin-left:auto" onclick="resetPorts()">&#8635;&nbsp;Ports zurücksetzen</button>
<button class="btn sm ghost danger" style="margin-left:.4rem" onclick="resetPorts()">&#8635;&nbsp;Ports zurücksetzen</button>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="pex-grid"> <div class="pex-grid">
@@ -2618,17 +2621,6 @@ body{background:var(--bg);color:var(--txt);font-family:-apple-system,BlinkMacSys
</div> </div>
<!-- Speicher-Panel -->
<div id="storage-panel" style="display:none;margin-top:.85rem;padding:.75rem;background:var(--bg2);border:1px solid var(--brd);border-radius:var(--r)">
<div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.65rem">
<span style="font-size:.82rem;font-weight:700;color:var(--txt)">&#128190;&nbsp;Speicherübersicht</span>
<button class="btn sm ghost" style="margin-left:auto" onclick="loadStorageInfo()">&#8635;&nbsp;Aktualisieren</button>
</div>
<div id="storage-list" style="display:flex;flex-direction:column;gap:.55rem">
<div style="color:var(--sub);font-size:.82rem">Wird geladen…</div>
</div>
</div>
<!-- Nicht zugewiesene Geräte --> <!-- Nicht zugewiesene Geräte -->
<div id="unassigned-wrap" style="display:none;margin-top:.85rem"> <div id="unassigned-wrap" style="display:none;margin-top:.85rem">
<div class="sec">Weitere verbundene Geräte</div> <div class="sec">Weitere verbundene Geräte</div>
@@ -2901,6 +2893,7 @@ body{background:var(--bg);color:var(--txt);font-family:-apple-system,BlinkMacSys
<div class="si-bar"><div class="si-fill ok" id="si-disk-bar" style="width:0%"></div></div> <div class="si-bar"><div class="si-fill ok" id="si-disk-bar" style="width:0%"></div></div>
</div> </div>
</div> </div>
<div id="storage-list" style="display:flex;flex-direction:column;gap:.4rem"></div>
<button class="btn" style="width:100%" onclick="checkUpdate()">🔍&nbsp;Nach Update suchen</button> <button class="btn" style="width:100%" onclick="checkUpdate()">🔍&nbsp;Nach Update suchen</button>
<div id="sys-update-flash" class="flash" style="display:none"></div> <div id="sys-update-flash" class="flash" style="display:none"></div>
<button class="btn" style="width:100%;background:rgba(220,60,60,.12);color:#e05555;border-color:rgba(220,60,60,.25)" onclick="rebootDevice()">&#8634;&nbsp;Gerät neu starten</button> <button class="btn" style="width:100%;background:rgba(220,60,60,.12);color:#e05555;border-color:rgba(220,60,60,.25)" onclick="rebootDevice()">&#8634;&nbsp;Gerät neu starten</button>
@@ -3874,60 +3867,47 @@ async function pollSysinfo(){
db.className='si-fill '+(s.disk_pct>=90?'hot':s.disk_pct>=75?'warn':'ok'); db.className='si-fill '+(s.disk_pct>=90?'hot':s.disk_pct>=75?'warn':'ok');
} }
}catch(e){} }catch(e){}
loadStorageInfo();
} }
// -- Speicher-Panel ----------------------------------------------------------- // -- Speicher (System-Karte) --------------------------------------------------
function fmtBytes(b){
if(b==null) return '';
if(b<1024**3) return (b/1024**2).toFixed(1)+' MB';
return (b/1024**3).toFixed(2)+' GB';
}
let storagePanelOpen=false;
function toggleStoragePanel(){
storagePanelOpen=!storagePanelOpen;
const p=$('storage-panel');
p.style.display=storagePanelOpen?'block':'none';
if(storagePanelOpen) loadStorageInfo();
}
async function loadStorageInfo(){ async function loadStorageInfo(){
const list=$('storage-list'); const list=$('storage-list');
list.innerHTML='<div style="color:var(--sub);font-size:.82rem">Wird geladen…</div>'; if(!list) return;
try{ try{
const items=await api('/storage-info'); const items=await api('/storage-info');
if(!items||!items.length){ if(!items||!items.length){ list.innerHTML=''; return; }
list.innerHTML='<div style="color:var(--sub);font-size:.82rem">Keine Geräte konfiguriert.</div>';
return;
}
list.innerHTML=items.map(it=>{ list.innerHTML=items.map(it=>{
const icon=it.type==='source'?'&#9650;':'&#9660;'; const roleColor=it.role==='source'?'var(--grn)':it.role==='dest'?'var(--acc)':'var(--sub)';
const typeLabel=it.type==='source'?'Quelle':'Ziel'; const roleIcon=it.role==='source'?'&#9650;':it.role==='dest'?'&#9660;':'&#9632;';
const color=it.type==='source'?'var(--grn)':'var(--acc)'; const roleLabel=it.role==='source'?'Quelle':it.role==='dest'?'Ziel':'Gerät';
if(!it.mounted||it.total==null){ const portStr=it.port==='__internal__'?'intern':'Port '+it.port;
return `<div style="display:flex;align-items:center;gap:.55rem;padding:.4rem .5rem;background:var(--surf);border:1px solid var(--brd);border-radius:.4rem"> if(it.total==null){
<span style="font-size:.8rem;color:${color}">${icon}</span> const fallback=it.size_str?it.size_str:'';
return `<div style="display:flex;align-items:center;gap:.5rem;padding:.35rem .45rem;background:var(--bg2);border:1px solid var(--brd);border-radius:.4rem">
<span style="font-size:.78rem;color:${roleColor};flex-shrink:0">${roleIcon}</span>
<div style="min-width:0;flex:1"> <div style="min-width:0;flex:1">
<div style="font-size:.79rem;font-weight:600;color:var(--txt)">${it.label}</div> <div style="font-size:.78rem;font-weight:600;color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${it.label}</div>
<div style="font-size:.73rem;color:var(--sub)">${typeLabel} · Port ${it.port==='__internal__'?'intern':it.port} · nicht verbunden</div> <div style="font-size:.71rem;color:var(--sub)">${roleLabel} · ${portStr} · ${fallback}</div>
</div> </div>
</div>`; </div>`;
} }
const pct=it.pct||0; const pct=it.pct||0;
const barCls=pct>=90?'hot':pct>=75?'warn':'ok'; const barCls=pct>=90?'hot':pct>=75?'warn':'ok';
return `<div style="padding:.45rem .5rem;background:var(--surf);border:1px solid var(--brd);border-radius:.4rem"> const barColor=barCls==='hot'?'var(--red)':barCls==='warn'?'var(--ylw)':'var(--grn)';
<div style="display:flex;align-items:center;gap:.55rem;margin-bottom:.35rem"> return `<div style="padding:.35rem .45rem;background:var(--bg2);border:1px solid var(--brd);border-radius:.4rem">
<span style="font-size:.8rem;color:${color}">${icon}</span> <div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.3rem">
<span style="font-size:.78rem;color:${roleColor};flex-shrink:0">${roleIcon}</span>
<div style="min-width:0;flex:1"> <div style="min-width:0;flex:1">
<div style="font-size:.79rem;font-weight:600;color:var(--txt)">${it.label}</div> <div style="font-size:.78rem;font-weight:600;color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${it.label}</div>
<div style="font-size:.73rem;color:var(--sub)">${typeLabel} · ${fmtBytes(it.used)} belegt · ${fmtBytes(it.free)} frei · ${fmtBytes(it.total)} gesamt</div> <div style="font-size:.71rem;color:var(--sub)">${roleLabel} · ${portStr} · ${fmtBytes(it.used)} / ${fmtBytes(it.total)} · ${fmtBytes(it.free)} frei</div>
</div> </div>
<span style="font-size:.76rem;font-weight:700;color:var(--${barCls==='hot'?'red':barCls==='warn'?'ylw':'grn'});flex-shrink:0">${pct}%</span> <span style="font-size:.74rem;font-weight:700;color:${barColor};flex-shrink:0">${pct}%</span>
</div> </div>
<div class="si-bar" style="margin:0"><div class="si-fill ${barCls}" style="width:${pct}%"></div></div> <div class="si-bar" style="margin:0"><div class="si-fill ${barCls}" style="width:${pct}%"></div></div>
</div>`; </div>`;
}).join(''); }).join('');
}catch(e){ }catch(e){}
list.innerHTML='<div style="color:var(--red);font-size:.82rem">Fehler beim Laden.</div>';
}
} }
// -- Kopier-Verlauf ----------------------------------------------------------- // -- Kopier-Verlauf -----------------------------------------------------------

View File

@@ -1 +1 @@
1.0.69 1.0.70