Add video reference tracking and display
- Add "Most referenced" sort option to sort by backlink count - Backend now supports sorting by referenced_by_count field - Search results now display reference counts as badges: - Shows number of backlinks (videos linking to this one) - Shows number of internal references (outbound links) - Reference badges appear alongside transcript source badges 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
const channelDropdown = document.getElementById("channelDropdown");
|
||||
const channelSummary = document.getElementById("channelSummary");
|
||||
const channelOptions = document.getElementById("channelOptions");
|
||||
const yearSel = document.getElementById("year");
|
||||
const sortSel = document.getElementById("sort");
|
||||
const sizeSel = document.getElementById("size");
|
||||
const exactToggle = document.getElementById("exactToggle");
|
||||
@@ -140,8 +141,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadYears() {
|
||||
if (!yearSel) return;
|
||||
try {
|
||||
const res = await fetch("/api/years");
|
||||
const data = await res.json();
|
||||
|
||||
// Keep the "All Years" option
|
||||
yearSel.innerHTML = '<option value="">All Years</option>';
|
||||
|
||||
data.forEach((item) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = item.Year;
|
||||
option.textContent = `${item.Year} (${item.Count})`;
|
||||
yearSel.appendChild(option);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to load years", err);
|
||||
}
|
||||
}
|
||||
|
||||
function setFromQuery() {
|
||||
qInput.value = qs.get("q") || "";
|
||||
yearSel.value = qs.get("year") || "";
|
||||
sortSel.value = qs.get("sort") || "relevant";
|
||||
sizeSel.value = qs.get("size") || "10";
|
||||
pendingChannelSelection = parseChannelParams(qs);
|
||||
@@ -305,13 +327,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateUrl(q, sort, channels, page, size, exact, fuzzy, phrase, queryMode) {
|
||||
function updateUrl(q, sort, channels, year, page, size, exact, fuzzy, phrase, queryMode) {
|
||||
const next = new URL(window.location.href);
|
||||
next.searchParams.set("q", q);
|
||||
next.searchParams.set("sort", sort);
|
||||
next.searchParams.delete("channel_id");
|
||||
next.searchParams.delete("channel");
|
||||
channels.forEach((id) => next.searchParams.append("channel_id", id));
|
||||
if (year) {
|
||||
next.searchParams.set("year", year);
|
||||
} else {
|
||||
next.searchParams.delete("year");
|
||||
}
|
||||
next.searchParams.set("page", page);
|
||||
next.searchParams.set("size", size);
|
||||
next.searchParams.set("exact", exact ? "1" : "0");
|
||||
@@ -893,7 +920,7 @@ function renderFrequencyChart(buckets, channelTotals) {
|
||||
freqChart.appendChild(legend);
|
||||
}
|
||||
|
||||
async function updateFrequencyChart(term, channels, queryMode) {
|
||||
async function updateFrequencyChart(term, channels, year, queryMode) {
|
||||
if (!freqChart || typeof d3 === "undefined") {
|
||||
return;
|
||||
}
|
||||
@@ -911,6 +938,9 @@ async function updateFrequencyChart(term, channels, queryMode) {
|
||||
params.set("term", trimmed);
|
||||
params.set("interval", "month");
|
||||
(channels || []).forEach((id) => params.append("channel_id", id));
|
||||
if (year) {
|
||||
params.set("year", year);
|
||||
}
|
||||
if (queryMode) {
|
||||
params.set("query_string", "1");
|
||||
}
|
||||
@@ -961,6 +991,13 @@ async function updateFrequencyChart(term, channels, queryMode) {
|
||||
const badges = [];
|
||||
if (item.highlightSource && item.highlightSource.primary) badges.push('primary transcript');
|
||||
if (item.highlightSource && item.highlightSource.secondary) badges.push('secondary transcript');
|
||||
|
||||
// Add reference count badges
|
||||
const refByCount = item.referenced_by_count || 0;
|
||||
const refToCount = item.internal_references_count || 0;
|
||||
if (refByCount > 0) badges.push(`${refByCount} backlink${refByCount !== 1 ? 's' : ''}`);
|
||||
if (refToCount > 0) badges.push(`${refToCount} reference${refToCount !== 1 ? 's' : ''}`);
|
||||
|
||||
const badgeHtml = badges.length
|
||||
? `<div class="badge-row">${badges
|
||||
.map((b) => `<span class="badge">${escapeHtml(b)}</span>` )
|
||||
@@ -1041,6 +1078,7 @@ async function updateFrequencyChart(term, channels, queryMode) {
|
||||
async function runSearch(pageOverride, pushState = true) {
|
||||
const q = qInput.value.trim();
|
||||
const channels = getSelectedChannels();
|
||||
const year = yearSel.value;
|
||||
const sort = sortSel.value;
|
||||
const size = parseInt(sizeSel.value, 10) || 10;
|
||||
const queryMode = queryToggle && queryToggle.checked;
|
||||
@@ -1062,7 +1100,7 @@ async function updateFrequencyChart(term, channels, queryMode) {
|
||||
currentPage = page;
|
||||
|
||||
if (pushState) {
|
||||
updateUrl(q, sort, channels, page, size, exact, fuzzy, phrase, queryMode);
|
||||
updateUrl(q, sort, channels, year, page, size, exact, fuzzy, phrase, queryMode);
|
||||
}
|
||||
|
||||
const params = new URLSearchParams();
|
||||
@@ -1075,17 +1113,19 @@ async function updateFrequencyChart(term, channels, queryMode) {
|
||||
params.set("phrase", phrase ? "1" : "0");
|
||||
params.set("query_string", queryMode ? "1" : "0");
|
||||
channels.forEach((id) => params.append("channel_id", id));
|
||||
if (year) params.set("year", year);
|
||||
|
||||
const res = await fetch(`/api/search?${params.toString()}`);
|
||||
const payload = await res.json();
|
||||
renderResults(payload, page);
|
||||
updateFrequencyChart(q, channels, queryMode);
|
||||
updateFrequencyChart(q, channels, year, queryMode);
|
||||
}
|
||||
|
||||
searchBtn.addEventListener("click", () => runSearch(0));
|
||||
qInput.addEventListener("keypress", (e) => {
|
||||
if (e.key === "Enter") runSearch(0);
|
||||
});
|
||||
yearSel.addEventListener("change", () => runSearch(0));
|
||||
sortSel.addEventListener("change", () => runSearch(0));
|
||||
sizeSel.addEventListener("change", () => runSearch(0));
|
||||
exactToggle.addEventListener("change", () => { rememberToggleState(); runSearch(0); });
|
||||
@@ -1104,6 +1144,7 @@ window.addEventListener("popstate", () => {
|
||||
|
||||
setFromQuery();
|
||||
loadMetrics();
|
||||
loadYears();
|
||||
loadChannels().then(() => runSearch(currentPage));
|
||||
})();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user