Compare commits
No commits in common. "be8776bbce62812d6ddbc062cd166f1faff058fd" and "096221619652cf91653639a03afbeee8889481b2" have entirely different histories.
be8776bbce
...
0962216196
3 changed files with 4 additions and 30 deletions
|
|
@ -45,8 +45,7 @@ async function list() {
|
||||||
(SELECT COUNT(*)::int FROM watched_collection_items i WHERE i.watch_id = w.id AND i.status IN ('imported', 'partial')) AS imported_count,
|
(SELECT COUNT(*)::int FROM watched_collection_items i WHERE i.watch_id = w.id AND i.status IN ('imported', 'partial')) AS imported_count,
|
||||||
(SELECT COUNT(*)::int FROM watched_collection_items i WHERE i.watch_id = w.id AND i.status = 'partial') AS partial_count,
|
(SELECT COUNT(*)::int FROM watched_collection_items i WHERE i.watch_id = w.id AND i.status = 'partial') AS partial_count,
|
||||||
(SELECT COUNT(*)::int FROM watched_collection_items i WHERE i.watch_id = w.id AND i.status = 'error') AS error_count,
|
(SELECT COUNT(*)::int FROM watched_collection_items i WHERE i.watch_id = w.id AND i.status = 'error') AS error_count,
|
||||||
COALESCE(r.recent, '[]'::json) AS recent,
|
COALESCE(r.recent, '[]'::json) AS recent
|
||||||
COALESCE(q.pending, '[]'::json) AS pending
|
|
||||||
FROM watched_collections w
|
FROM watched_collections w
|
||||||
LEFT JOIN projects p ON p.id = w.project_id
|
LEFT JOIN projects p ON p.id = w.project_id
|
||||||
LEFT JOIN LATERAL (
|
LEFT JOIN LATERAL (
|
||||||
|
|
@ -55,13 +54,6 @@ async function list() {
|
||||||
ORDER BY i.seen_at DESC) AS recent
|
ORDER BY i.seen_at DESC) AS recent
|
||||||
FROM (SELECT * FROM watched_collection_items WHERE watch_id = w.id ORDER BY seen_at DESC LIMIT 5) i
|
FROM (SELECT * FROM watched_collection_items WHERE watch_id = w.id ORDER BY seen_at DESC LIMIT 5) i
|
||||||
) r ON true
|
) r ON true
|
||||||
-- Every model still waiting for its files, not just the five most recent.
|
|
||||||
LEFT JOIN LATERAL (
|
|
||||||
SELECT json_agg(json_build_object('model_id', i.model_id, 'title', i.title) ORDER BY i.seen_at DESC) AS pending
|
|
||||||
FROM (SELECT * FROM watched_collection_items
|
|
||||||
WHERE watch_id = w.id AND status = 'partial' AND model_id IS NOT NULL
|
|
||||||
ORDER BY seen_at DESC LIMIT 200) i
|
|
||||||
) q ON true
|
|
||||||
ORDER BY w.id`);
|
ORDER BY w.id`);
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
@ -96,14 +88,8 @@ async function add({ url, aiTag = false, intervalHours = 6 }) {
|
||||||
`INSERT INTO watched_collections (site, source_id, url, title, project_id, ai_tag, interval_hours, total, listed, last_status)
|
`INSERT INTO watched_collections (site, source_id, url, title, project_id, ai_tag, interval_hours, total, listed, last_status)
|
||||||
VALUES ('makerworld', $1, $2, $3, $4, $5, $6, $7, $8, 'checking') RETURNING id`,
|
VALUES ('makerworld', $1, $2, $3, $4, $5, $6, $7, $8, 'checking') RETURNING id`,
|
||||||
[sourceId, url, collection.title, project.id, Boolean(aiTag), hours, collection.total, collection.designs.length]);
|
[sourceId, url, collection.title, project.id, Boolean(aiTag), hours, collection.total, collection.designs.length]);
|
||||||
// The collection was just read, so import straight from it rather than
|
checkNow(watch.id).catch((err) => console.warn(`Watch ${watch.id} first check failed:`, err.message));
|
||||||
// fetching the whole thing again.
|
return get(watch.id);
|
||||||
const added = await get(watch.id);
|
|
||||||
checking.add(watch.id);
|
|
||||||
importFound(added, collection)
|
|
||||||
.catch((err) => console.warn(`Watch ${watch.id} first import failed:`, err.message))
|
|
||||||
.finally(() => checking.delete(watch.id));
|
|
||||||
return added;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(id, { enabled, aiTag, intervalHours }) {
|
async function update(id, { enabled, aiTag, intervalHours }) {
|
||||||
|
|
|
||||||
|
|
@ -2204,12 +2204,6 @@ function watchHtml(w) {
|
||||||
? `<button type="button" class="link-btn" data-open-model="${i.model_id}">${escapeHtml(i.model_name || i.title || `Model ${i.model_id}`)}</button>`
|
? `<button type="button" class="link-btn" data-open-model="${i.model_id}">${escapeHtml(i.model_name || i.title || `Model ${i.model_id}`)}</button>`
|
||||||
: escapeHtml(i.title || i.design_id)}${i.error ? ` <span class="ai-run-error">${escapeHtml(i.error)}</span>` : ''}${(i.error || i.status === 'partial') && i.model_id
|
: escapeHtml(i.title || i.design_id)}${i.error ? ` <span class="ai-run-error">${escapeHtml(i.error)}</span>` : ''}${(i.error || i.status === 'partial') && i.model_id
|
||||||
? ` <button type="button" class="link-btn" data-watch="files" data-model-id="${i.model_id}">Get files</button>` : ''}</span>`).join('')}</div>` : ''}
|
? ` <button type="button" class="link-btn" data-watch="files" data-model-id="${i.model_id}">Get files</button>` : ''}</span>`).join('')}</div>` : ''}
|
||||||
${(w.pending || []).length ? `<details class="watch-pending">
|
|
||||||
<summary>${plural(w.partial_count || w.pending.length, 'model')} still without files</summary>
|
|
||||||
<div class="watch-recent">${w.pending.map((i) => `
|
|
||||||
<span><button type="button" class="link-btn" data-open-model="${i.model_id}">${escapeHtml(i.title || `Model ${i.model_id}`)}</button>
|
|
||||||
<button type="button" class="link-btn" data-watch="files" data-model-id="${i.model_id}">Get files</button></span>`).join('')}</div>
|
|
||||||
</details>` : ''}
|
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2236,10 +2230,7 @@ async function refreshWatches() {
|
||||||
list.innerHTML = watches.length
|
list.innerHTML = watches.length
|
||||||
? watches.map(watchHtml).join('')
|
? watches.map(watchHtml).join('')
|
||||||
: '<p class="form-hint">Nothing watched yet. Paste a MakerWorld collection link above.</p>';
|
: '<p class="form-hint">Nothing watched yet. Paste a MakerWorld collection link above.</p>';
|
||||||
// Keep the page current even between checks: one may start from the schedule,
|
if (watches.some((w) => w.last_status === 'checking')) watchPoll = setTimeout(refreshWatches, 5000);
|
||||||
// another tab, or another browser. refreshWatches stops itself when you leave.
|
|
||||||
const checking = watches.some((w) => w.last_status === 'checking');
|
|
||||||
watchPoll = setTimeout(refreshWatches, checking ? 5000 : 20000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleAddWatch(e) {
|
async function handleAddWatch(e) {
|
||||||
|
|
|
||||||
|
|
@ -464,9 +464,6 @@ label { display: block; font-size: 13px; font-weight: 500; color: var(--text-mut
|
||||||
.watch-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px; }
|
.watch-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px; }
|
||||||
.watch-actions { margin-left: auto; display: flex; flex-wrap: wrap; gap: 8px; }
|
.watch-actions { margin-left: auto; display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
.watch-actions .btn-sm { align-self: auto; }
|
.watch-actions .btn-sm { align-self: auto; }
|
||||||
.watch-pending summary { cursor: pointer; font-size: 13px; color: var(--text-muted); }
|
|
||||||
.watch-pending summary:hover { color: var(--text); }
|
|
||||||
.watch-pending .watch-recent { margin-top: 6px; }
|
|
||||||
.watch-meta { font-family: var(--mono); font-size: 12px; color: var(--text-dim); }
|
.watch-meta { font-family: var(--mono); font-size: 12px; color: var(--text-dim); }
|
||||||
.watch-recent { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--text-muted); max-height: 180px; overflow-y: auto; }
|
.watch-recent { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--text-muted); max-height: 180px; overflow-y: auto; }
|
||||||
#watchStatus.is-error { color: var(--danger); }
|
#watchStatus.is-error { color: var(--danger); }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue