Add refresh loras

This commit is contained in:
Will Miao
2025-01-26 23:32:17 +08:00
parent 72e121c145
commit 0c80555cc7
4 changed files with 79 additions and 17 deletions

View File

@@ -161,6 +161,7 @@ class LorasEndpoint:
"folder": lora["folder"], "folder": lora["folder"],
"sha256": lora["sha256"], "sha256": lora["sha256"],
"file_path": lora["file_path"], "file_path": lora["file_path"],
"modified": lora["modified"],
"civitai": lora.get("civitai", {}) or {} # 确保当 civitai 为 None 时返回空字典 "civitai": lora.get("civitai", {}) or {} # 确保当 civitai 为 None 时返回空字典
} }
except Exception as e: except Exception as e:
@@ -174,6 +175,7 @@ class LorasEndpoint:
"folder": lora.get("folder", ""), "folder": lora.get("folder", ""),
"sha256": lora.get("sha256", ""), "sha256": lora.get("sha256", ""),
"file_path": lora.get("file_path", ""), "file_path": lora.get("file_path", ""),
"modified": lora.get("modified", ""),
"civitai": { "civitai": {
"id": "", "id": "",
"modelId": "", "modelId": "",
@@ -289,14 +291,17 @@ class LorasEndpoint:
# 更新模型名称优先使用CivitAI名称 # 更新模型名称优先使用CivitAI名称
if 'model' in civitai_metadata: if 'model' in civitai_metadata:
local_metadata['model_name'] = civitai_metadata['model'].get('name', local_metadata.get('model_name')) local_metadata['model_name'] = civitai_metadata['model'].get('name', local_metadata.get('model_name'))
# update base model
local_metadata['base_model'] = civitai_metadata.get('baseModel')
# 4. 下载预览图 # 4. 下载预览图
first_image = next((img for img in civitai_metadata.get('images', []) if img.get('type') == 'image'), None) first_preview = next((img for img in civitai_metadata.get('images', [])), None)
if first_image: if first_preview:
preview_extension = os.path.splitext(first_image['url'])[-1] # Get the image file extension
preview_extension = '.mp4' if first_preview['type'] == 'video' else os.path.splitext(first_preview['url'])[-1] # Get the file extension
preview_filename = os.path.splitext(os.path.basename(data['file_path']))[0] + preview_extension preview_filename = os.path.splitext(os.path.basename(data['file_path']))[0] + preview_extension
preview_path = os.path.join(os.path.dirname(data['file_path']), preview_filename) preview_path = os.path.join(os.path.dirname(data['file_path']), preview_filename)
await client.download_preview_image(first_image['url'], preview_path) await client.download_preview_image(first_preview['url'], preview_path)
# 存储相对路径,使用正斜杠格式 # 存储相对路径,使用正斜杠格式
local_metadata['preview_url'] = os.path.relpath(preview_path, self.loras_root).replace(os.sep, '/') local_metadata['preview_url'] = os.path.relpath(preview_path, self.loras_root).replace(os.sep, '/')

View File

@@ -2,15 +2,13 @@
function sortCards(sortBy) { function sortCards(sortBy) {
const grid = document.getElementById('loraGrid'); const grid = document.getElementById('loraGrid');
const cards = Array.from(grid.children); const cards = Array.from(grid.children);
cards.sort((a, b) => { cards.sort((a, b) => {
switch(sortBy) { switch(sortBy) {
case 'name': case 'name':
return a.dataset.name.localeCompare(b.dataset.name); return a.dataset.name.localeCompare(b.dataset.name);
case 'date': case 'date':
return new Date(b.dataset.date) - new Date(a.dataset.date); return b.dataset.modified - a.dataset.modified;
case 'size':
return parseFloat(b.dataset.size) - parseFloat(a.dataset.size);
} }
}); });
@@ -19,13 +17,59 @@ function sortCards(sortBy) {
// 刷新功能 // 刷新功能
async function refreshLoras() { async function refreshLoras() {
const loadingOverlay = document.getElementById('loading-overlay');
const loraGrid = document.getElementById('loraGrid');
const currentSort = document.getElementById('sortSelect').value;
const activeFolder = document.querySelector('.tag.active')?.dataset.folder;
try { try {
// Show loading overlay
loadingOverlay.style.display = 'flex';
// Fetch new data
const response = await fetch('/loras?refresh=true'); const response = await fetch('/loras?refresh=true');
if (response.ok) { if (!response.ok) throw new Error('Refresh failed');
location.reload();
// Parse the HTML response
const parser = new DOMParser();
const doc = parser.parseFromString(await response.text(), 'text/html');
// Get the new lora cards
const newLoraGrid = doc.getElementById('loraGrid');
// Update the grid content
loraGrid.innerHTML = newLoraGrid.innerHTML;
// Re-attach click listeners to new cards
document.querySelectorAll('.lora-card').forEach(card => {
card.addEventListener('click', () => {
const meta = JSON.parse(card.dataset.meta || '{}');
if (Object.keys(meta).length > 0) {
showModal(meta);
}
});
});
// Re-apply current sorting
sortCards(currentSort);
// Re-apply current folder filter if any
if (activeFolder) {
document.querySelectorAll('.lora-card').forEach(card => {
if (card.getAttribute('data-folder') === activeFolder) {
card.style.display = '';
} else {
card.style.display = 'none';
}
});
} }
} catch (error) { } catch (error) {
console.error('Refresh failed:', error); console.error('Refresh failed:', error);
alert('Failed to refresh loras');
} finally {
// Hide loading overlay
loadingOverlay.style.display = 'none';
} }
} }
@@ -94,10 +138,16 @@ async function deleteModel(fileName) {
} }
// 初始化排序 // 初始化排序
document.getElementById('sortSelect').addEventListener('change', (e) => { document.getElementById('sortSelect')?.addEventListener('change', (e) => {
sortCards(e.target.value); sortCards(e.target.value);
}); });
// 立即执行初始排序
const sortSelect = document.getElementById('sortSelect');
if (sortSelect) {
sortCards(sortSelect.value);
}
// 添加搜索功能 // 添加搜索功能
document.getElementById('searchInput')?.addEventListener('input', (e) => { document.getElementById('searchInput')?.addEventListener('input', (e) => {
const term = e.target.value.toLowerCase(); const term = e.target.value.toLowerCase();
@@ -123,9 +173,9 @@ function showModal(lora) {
const modal = document.getElementById('loraModal'); const modal = document.getElementById('loraModal');
modal.innerHTML = ` modal.innerHTML = `
<div class="modal-content"> <div class="modal-content">
<h2>${lora.name}</h2> <h2>${lora.model.name}</h2>
<div class="carousel"> <div class="carousel">
${lora.images.map(img => `<img src="${img}" alt="Preview">`).join('')} ${lora.images.map(img => `<img src="${img.url}" alt="Preview">`).join('')}
</div> </div>
<div class="description">${lora.description}</div> <div class="description">${lora.description}</div>
<button class="close" onclick="closeModal()">&times;</button> <button class="close" onclick="closeModal()">&times;</button>

View File

@@ -37,7 +37,6 @@
<select id="sortSelect"> <select id="sortSelect">
<option value="name">Name</option> <option value="name">Name</option>
<option value="date">Date</option> <option value="date">Date</option>
<option value="size">Size</option>
</select> </select>
<button onclick="refreshLoras()"><i class="fas fa-sync"></i> Refresh</button> <button onclick="refreshLoras()"><i class="fas fa-sync"></i> Refresh</button>
<button onclick="fetchCivitai()" class="secondary"><i class="fas fa-download"></i> Fetch</button> <button onclick="fetchCivitai()" class="secondary"><i class="fas fa-download"></i> Fetch</button>
@@ -54,9 +53,17 @@
data-name="{{ lora.model_name }}" data-name="{{ lora.model_name }}"
data-file_name="{{ lora.file_name }}" data-file_name="{{ lora.file_name }}"
data-folder="{{ lora.folder }}" data-folder="{{ lora.folder }}"
data-modified="{{ lora.modified }}"
data-meta="{{ lora.civitai | default({}) | tojson | forceescape }}"> data-meta="{{ lora.civitai | default({}) | tojson | forceescape }}">
<div class="card-preview"> <div class="card-preview">
<img src="{{ ('/loras_static/previews/' + lora.preview_url) if lora.preview_url else '/loras_static/images/no-preview.png' }}" alt="{{ lora.name }}"> {% if lora.preview_url.endswith('.mp4') or lora.preview_url.endswith('.webm') %}
<video controls>
<source src="{{ '/loras_static/previews/' + lora.preview_url }}" type="video/mp4">
Your browser does not support the video tag.
</video>
{% else %}
<img src="{{ ('/loras_static/previews/' + lora.preview_url) if lora.preview_url else '/loras_static/images/no-preview.png' }}" alt="{{ lora.name }}">
{% endif %}
<div class="card-header"> <div class="card-header">
<span class="base-model-label" title="{{ lora.base_model }}"> <span class="base-model-label" title="{{ lora.base_model }}">
{{ lora.base_model }} {{ lora.base_model }}
@@ -89,6 +96,6 @@
</div> </div>
</div> </div>
<script src="/loras_static/js/script.js"></script> <script src="/loras_static/js/script.js" defer></script>
</body> </body>
</html> </html>

View File

@@ -6,7 +6,7 @@ BASE_MODEL_MAPPING = {
"sd-v2-1": "SD2.1", "sd-v2-1": "SD2.1",
"sdxl": "SDXL", "sdxl": "SDXL",
"sd-v2": "SD2.0", "sd-v2": "SD2.0",
"flux1": "Flux1.D", "flux1": "Flux.1 D",
} }
def determine_base_model(version_string: Optional[str]) -> str: def determine_base_model(version_string: Optional[str]) -> str: