Switch search backend to SearXNG
build-push / docker (push) Has been cancelled

This commit is contained in:
AI Assistant
2026-03-13 10:10:13 +09:00
parent 6734887fc6
commit ee316de7ab
8 changed files with 466 additions and 199 deletions
+72 -7
View File
@@ -4,13 +4,12 @@ const searchForm = document.getElementById("searchForm");
const searchQuery = document.getElementById("searchQuery");
const searchResults = document.getElementById("searchResults");
const searchWarning = document.getElementById("searchWarning");
const queryVariants = document.getElementById("queryVariants");
const dropzone = document.getElementById("dropzone");
const fileInput = document.getElementById("fileInput");
const uploadResult = document.getElementById("uploadResult");
const downloadForm = document.getElementById("downloadForm");
const downloadUrl = document.getElementById("downloadUrl");
const startTime = document.getElementById("startTime");
const endTime = document.getElementById("endTime");
const downloadResult = document.getElementById("downloadResult");
const cardTemplate = document.getElementById("searchCardTemplate");
const previewModal = document.getElementById("previewModal");
@@ -21,6 +20,11 @@ const previewDuration = document.getElementById("previewDuration");
const qualitySelect = document.getElementById("qualitySelect");
const confirmDownload = document.getElementById("confirmDownload");
const closePreviewModal = document.getElementById("closePreviewModal");
const startRange = document.getElementById("startRange");
const endRange = document.getElementById("endRange");
const rangeSummary = document.getElementById("rangeSummary");
const setStartFromPreview = document.getElementById("setStartFromPreview");
const setEndFromPreview = document.getElementById("setEndFromPreview");
let pendingDownload = null;
@@ -29,6 +33,44 @@ function setStatus(label, progress) {
statusBar.style.width = `${Math.max(0, Math.min(100, progress))}%`;
}
function toClock(totalSeconds) {
const seconds = Math.max(0, Math.floor(Number(totalSeconds) || 0));
const hours = String(Math.floor(seconds / 3600)).padStart(2, "0");
const minutes = String(Math.floor((seconds % 3600) / 60)).padStart(2, "0");
const secs = String(seconds % 60).padStart(2, "0");
return `${hours}:${minutes}:${secs}`;
}
function syncRanges() {
let start = Number(startRange.value || 0);
let end = Number(endRange.value || 0);
if (start > end) {
if (document.activeElement === startRange) {
end = start;
endRange.value = String(end);
} else {
start = end;
startRange.value = String(start);
}
}
rangeSummary.textContent = `${toClock(start)} - ${toClock(end)}`;
}
function renderQueryVariants(queries = []) {
queryVariants.innerHTML = "";
if (!queries.length) {
queryVariants.classList.add("hidden");
return;
}
for (const item of queries) {
const badge = document.createElement("span");
badge.className = "rounded-full border border-white/10 bg-white/[0.03] px-3 py-1 text-xs uppercase tracking-[0.18em] text-zinc-300";
badge.textContent = item;
queryVariants.appendChild(badge);
}
queryVariants.classList.remove("hidden");
}
function connectWS() {
const protocol = window.location.protocol === "https:" ? "wss" : "ws";
const socket = new WebSocket(`${protocol}://${window.location.host}/ws`);
@@ -68,10 +110,14 @@ async function api(path, options = {}) {
function renderResults(results) {
searchResults.innerHTML = "";
if (!results.length) {
searchResults.innerHTML = `<div class="rounded-3xl border border-white/10 bg-black/30 p-5 text-sm text-zinc-400">No results matched the current search sources.</div>`;
return;
}
for (const item of results) {
const node = cardTemplate.content.firstElementChild.cloneNode(true);
node.href = item.link;
node.querySelector("img").src = item.thumbnailUrl;
node.querySelector("img").src = item.thumbnailUrl || "https://placehold.co/1280x720/0a0a0a/ffffff?text=No+Preview";
node.querySelector("img").alt = item.title;
node.querySelector("h3").textContent = item.title;
node.querySelector("p").textContent = item.reason;
@@ -91,6 +137,7 @@ searchForm.addEventListener("submit", async (event) => {
body: JSON.stringify({ query: searchQuery.value }),
});
renderResults(data.results || []);
renderQueryVariants(data.queries || []);
if (data.warning) {
searchWarning.textContent = data.warning;
searchWarning.classList.remove("hidden");
@@ -99,6 +146,7 @@ searchForm.addEventListener("submit", async (event) => {
} catch (error) {
searchWarning.textContent = error.message;
searchWarning.classList.remove("hidden");
renderQueryVariants([]);
setStatus("search failed", 100);
}
});
@@ -137,8 +185,12 @@ function openPreviewModal(preview) {
option.textContent = item.label;
qualitySelect.appendChild(option);
}
startTime.value = preview.startDefault || "00:00:00";
endTime.value = preview.endDefault || "00:00:00";
const maxDuration = Number(preview.durationSeconds || 0);
startRange.max = String(maxDuration);
endRange.max = String(maxDuration);
startRange.value = "0";
endRange.value = String(maxDuration);
syncRanges();
previewModal.classList.remove("hidden");
previewModal.classList.add("flex");
}
@@ -149,6 +201,9 @@ function closeModal() {
previewVideo.load();
previewModal.classList.add("hidden");
previewModal.classList.remove("flex");
startRange.value = "0";
endRange.value = "0";
syncRanges();
pendingDownload = null;
}
@@ -214,8 +269,8 @@ confirmDownload.addEventListener("click", async () => {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
url: pendingDownload.url,
start: startTime.value,
end: endTime.value,
start: toClock(startRange.value),
end: toClock(endRange.value),
quality: qualitySelect.value,
force: pendingDownload.force,
}),
@@ -233,6 +288,16 @@ previewModal.addEventListener("click", (event) => {
closeModal();
}
});
startRange.addEventListener("input", syncRanges);
endRange.addEventListener("input", syncRanges);
setStartFromPreview.addEventListener("click", () => {
startRange.value = String(Math.floor(previewVideo.currentTime || 0));
syncRanges();
});
setEndFromPreview.addEventListener("click", () => {
endRange.value = String(Math.floor(previewVideo.currentTime || 0));
syncRanges();
});
connectWS();
setStatus("idle", 0);