tone3000 metadata capture: save .meta.json alongside downloaded models

- UI now sends full Tone3000 metadata on install (id, author,
  architecture, pack_name, description, thumbnail, etc.)
- Search results show size, architecture, and pack name
- Install endpoint saves a .meta.json file next to each .nam with:
  author, architecture, description, pack, thumbnail, download date
- Enables offline browsing of model provenance
This commit is contained in:
2026-06-16 19:49:23 -04:00
parent a37e381c4e
commit 5cdc751312
2 changed files with 21 additions and 2 deletions
+19
View File
@@ -1950,6 +1950,25 @@ class WebServer:
path = await client.download_model(model)
if not path:
raise HTTPException(status_code=500, detail="Download failed")
# Save metadata alongside the model file
import json
meta = {
"id": data.get("id"),
"name": data.get("name"),
"filename": data.get("filename"),
"size_bytes": data.get("size"),
"author": data.get("author"),
"author_avatar": data.get("author_avatar"),
"thumbnail": data.get("thumbnail"),
"tone_description": data.get("tone_description"),
"pack_name": data.get("pack_name"),
"architecture": data.get("architecture"),
"created_at": data.get("created_at"),
"download_date": datetime.datetime.now().isoformat(),
"source": "tone3000",
}
meta_path = path.with_suffix(".meta.json")
meta_path.write_text(json.dumps(meta, indent=2))
# Refresh the model list via NAM host
nam = self.deps.nam_host
if nam:
+2 -2
View File
@@ -1021,10 +1021,10 @@ function init(){
const d=document.createElement('div');
d.className='preset-card';
d.style.cursor='default';
d.innerHTML='<div class="pc-name">'+(item.name||'Unknown')+'</div><div class="pc-num">'+(item.author||'')+'</div><button class="sr-action primary" style="margin-top:4px;align-self:flex-end">Get</button>';
d.innerHTML='<div class="pc-name">'+(item.name||'Unknown')+'</div><div class="pc-num">'+(item.author||'')+(item.pack_name?' · '+item.pack_name:'')+'</div><div style="font-size:8px;color:var(--text-dim);margin-top:2px">'+(item.size_display||'')+(item.architecture?' · '+item.architecture:'')+'</div><button class="sr-action primary" style="margin-top:4px;align-self:flex-end">Get</button>';
d.querySelector('button').onclick=async()=>{
d.querySelector('button').textContent='...';
try{await API._fetch('/api/models/tonedownload/install',{method:'POST',body:JSON.stringify({download_url:item.download_url,name:item.name}),timeout:60000});toast('Downloaded!');d.querySelector('button').textContent='✓';}catch(e){toast(e.message);d.querySelector('button').textContent='Retry';}
try{await API._fetch('/api/models/tonedownload/install',{method:'POST',body:JSON.stringify({id:item.id,download_url:item.download_url,name:item.name,filename:item.filename,size:item.size,author:item.author,author_avatar:item.author_avatar,thumbnail:item.thumbnail,tone_description:item.tone_description,pack_name:item.pack_name,architecture:item.architecture,created_at:item.created_at}),timeout:60000});toast('Downloaded!');d.querySelector('button').textContent='✓';}catch(e){toast(e.message);d.querySelector('button').textContent='Retry';}
};
res.appendChild(d);
});