From 6abc1e23c7c5af5681164e931ec73f40eb756e18 Mon Sep 17 00:00:00 2001 From: Tobias Leuschner Date: Sat, 9 May 2026 12:01:32 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Unterst=C3=BCtzung=20f=C3=BCr=20die=20A?= =?UTF-8?q?uswahl=20mehrerer=20Quellger=C3=A4te=20hinzugef=C3=BCgt=20und?= =?UTF-8?q?=20Versionsnummer=20auf=201.0.24=20erh=C3=B6ht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 66 +++++++++++++++++++++++++++++++++++++++++++---------- version.txt | 2 +- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/app.py b/app.py index 950ea5a..0cd30f0 100644 --- a/app.py +++ b/app.py @@ -983,9 +983,13 @@ def r_start(): return jsonify(error='Abbruch wird noch abgeschlossen - bitte kurz warten und erneut versuchen.'), 400 cfg = load_cfg() devs = usb_devices() + body = request.get_json(force=True) or {} + wanted_ports = body.get('ports') # None = alle konfigurierten Quellen src_ports = _resolve_source_ports(cfg) srcs = [next((d for d in devs if d['usb_port'] == sp['port']), None) for sp in src_ports] srcs = [s for s in srcs if s is not None] + if wanted_ports is not None: + srcs = [s for s in srcs if s['usb_port'] in wanted_ports] if not srcs: return jsonify(error='Keine Quellgeräte gefunden (Ports nicht verbunden)'), 400 dst = next((d for d in devs if d['usb_port'] == cfg.get('dest_port')), None) if not dst: return jsonify(error='Zielgerät nicht gefunden'), 400 @@ -1726,9 +1730,9 @@ body{background:var(--bg);color:var(--txt);font-family:-apple-system,BlinkMacSys
- - - +
+ +
@@ -2004,6 +2008,8 @@ async function refreshDevices(){ populateSel(); } +let selectedPortSet = new Set(); + function renderSources(){ const ports = cfg.source_ports || []; $('sources-list').innerHTML = ports.map((sp, i) => { @@ -2011,6 +2017,7 @@ function renderSources(){ const info = dev ? (dev.label||dev.device) + (dev.size ? ' | '+dev.size : '') : 'Gerät nicht verbunden'; + const chk = selectedPortSet.has(sp.port) ? 'checked' : ''; return `
▲ Quelle ${i+1}${sp.label?' – '+sp.label:''}
@@ -2019,13 +2026,36 @@ function renderSources(){
Port ${sp.port}
${info}
-
`; }).join('') + (ports.length === 0 ? '
Noch keine Quelle konfiguriert.
' : ''); + renderExplorerTabs(); +} + +function toggleSrc(port, on){ + if(on) selectedPortSet.add(port); else selectedPortSet.delete(port); +} + +function renderExplorerTabs(){ + const ports = cfg.source_ports || []; + $('src-tabs').innerHTML = ports.map((sp, i) => { + const r = 'src_'+i; + const label = sp.label || ('Quelle '+(i+1)); + return ``; + }).join(''); + // Fallback: falls aktive Rolle nicht mehr existiert + if(expl.role!=='dst' && !ports.some((_,i)=>expl.role==='src_'+i)){ + expl.role = ports.length>0 ? 'src_0' : 'dst'; + } } function renderSlot(r, port, label){ @@ -2081,6 +2111,7 @@ async function addSource(){ if(port===cfg.dest_port){flash('src-flash','err','Port bereits als Ziel konfiguriert!');return;} if((cfg.source_ports||[]).some(sp=>sp.port===port)){flash('src-flash','err','Port bereits als Quelle hinzugefügt!');return;} cfg.source_ports = [...(cfg.source_ports||[]), {port, label}]; + selectedPortSet.add(port); await api('/config','POST',cfg); $('src-label').value=''; flash('src-flash','ok','✓ Quelle Port '+port+' hinzugefügt.'); @@ -2089,6 +2120,7 @@ async function addSource(){ async function removeSource(port){ cfg.source_ports = (cfg.source_ports||[]).filter(sp=>sp.port!==port); + selectedPortSet.delete(port); await api('/config','POST',cfg); renderSources(); populateSel(); renderUnassigned(); } @@ -2113,7 +2145,9 @@ async function assignPort(role){ async function startCopy(){ _dismissed=false; if(_autoDismissTimer){ clearTimeout(_autoDismissTimer); _autoDismissTimer=null; } - const r=await api('/copy/start','POST'); + const ports=[...(cfg.source_ports||[]).map(sp=>sp.port).filter(p=>selectedPortSet.has(p))]; + if(!ports.length){flash('copy-hint','warn','Keine Quelle ausgewählt – bitte mindestens eine Quelle anhaken.');return;} + const r=await api('/copy/start','POST',{ports}); if(r.error) flash('copy-hint','warn',r.error); else $('copy-hint').style.display='none'; } @@ -2126,6 +2160,8 @@ async function loadCfg(){ if(!cfg.source_ports) cfg.source_ports=[]; if(cfg.source_ports.length===0 && cfg.source_port) cfg.source_ports=[{port:cfg.source_port, label:cfg.source_label||''}]; + // Alle konfigurierten Quellen standardmäßig ausgewählt + selectedPortSet = new Set(cfg.source_ports.map(sp=>sp.port)); $('c-fmt').value=cfg.folder_format||'%Y-%m-%d'; $('c-time').checked=!!cfg.add_time; $('c-sub').checked=!!cfg.subfolder; $('c-auto').checked=!!cfg.auto_copy; $('c-filter').value=cfg.file_filter||''; @@ -2251,17 +2287,23 @@ async function utDel(id,name){ // -- File Explorer ------------------------------------------------------------- const expl={ - role:'src', paths:{src:'',dst:''}, + role:'src_0', paths:{dst:''}, switchRole(r){ this.role=r; - $('etab-src').classList.toggle('on',r==='src'); - $('etab-dst').classList.toggle('on',r==='dst'); - this.load(this.paths[r]); + document.querySelectorAll('.etab').forEach(t=>t.classList.remove('on')); + const tab=$('etab-'+r); if(tab) tab.classList.add('on'); + this.load(this.paths[r]||''); }, - reload(){this.load(this.paths[this.role]);}, + reload(){this.load(this.paths[this.role]||'');}, navigate(p){this.load(p);}, async load(path=''){ - const port=this.role==='src'?(cfg.source_ports&&cfg.source_ports[0]?.port):cfg.dest_port; + let port; + if(this.role==='dst'){ + port=cfg.dest_port; + } else { + const idx=parseInt(this.role.replace('src_',''),10); + port=cfg.source_ports&&cfg.source_ports[idx]?cfg.source_ports[idx].port:null; + } const body=$('expl-body'), bread=$('expl-bread'); if(!port){body.innerHTML='
Kein Port konfiguriert
';bread.innerHTML='';return;} const dev=devs.find(d=>d.usb_port===port); @@ -2270,7 +2312,7 @@ const expl={ try{ const data=await api('/browse?port='+encodeURIComponent(port)+'&path='+encodeURIComponent(path)); if(data.error){body.innerHTML='
⚠ '+data.error+'
';return;} - this.paths[this.role]=data.path||''; + this.paths[this.role]=data.path||''; // role z.B. 'src_0', 'dst' this._bread(data.path||'',dev.label||dev.device); this._list(data.entries||[],data.path||''); }catch(e){body.innerHTML='
Verbindungsfehler
';} diff --git a/version.txt b/version.txt index 1c2de38..321816a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.23 \ No newline at end of file +1.0.24 \ No newline at end of file