Docs/Skills and code/src/
src/transcript-sampling.js
import path from "node:path";
export const TRANSCRIPT_ROOT = path.resolve(process.cwd(), "transcripts");
export const SAMPLE_FILE_PATTERN = /^(best|median|worst)-\d+_.+\.txt$/;
// A video this young hasn't had time to accumulate views, so it can't fairly be called "worst".
export const MIN_AGE_DAYS = 14;
// Views/day divides by at least this many days so a 3-week-old hit isn't ranked as if it were a 1-day-old rocket.
export const AGE_FLOOR_DAYS = 30;
const MIN_ELIGIBLE = 10;
/** Windows-safe folder name for a channel, keeping non-Latin letters (Hindi, etc.) intact. */
export function channelFolderName(name) {
const cleaned = String(name ?? "")
.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "")
.replace(/\s+/g, " ")
.trim()
.replace(/[. ]+$/, "")
.slice(0, 80);
return cleaned || "channel";
}
export function seededRandom(seed) {
let a = seed >>> 0;
return () => {
a = (a + 0x6d2b79f5) >>> 0;
let t = a;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
function shuffle(items, rng) {
const out = [...items];
for (let i = out.length - 1; i > 0; i--) {
const j = Math.floor(rng() * (i + 1));
[out[i], out[j]] = [out[j], out[i]];
}
return out;
}
function median(sorted) {
if (!sorted.length) return null;
const mid = Math.floor(sorted.length / 2);
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
}
/**
* Ranks a channel's videos by views per day (age floored, see AGE_FLOOR_DAYS), which is fairer than raw
* lifetime views: raw views would make the newest uploads look like the channel's worst performers.
*
* Returns ordered candidate lists per bucket so a caller can fall through to the next candidate when a
* video has no transcript:
* best - highest views/day first
* worst - lowest views/day first
* median - random picks from the middle 30% of the ranking, then the rest by distance from the middle
*/
export function rankAndBucket(videos, { now = Date.now(), rng = Math.random } = {}) {
const dated = videos
.filter(v => Number.isFinite(Number(v.view_count)) && v.published_at_normalized && Number.isFinite(Date.parse(v.published_at_normalized)))
.map(v => ({ ...v, view_count: Number(v.view_count), age_days: (now - Date.parse(v.published_at_normalized)) / 86400000 }));
let pool = dated.filter(v => v.age_days >= MIN_AGE_DAYS);
let usedFallbackPool = false;
if (pool.length < MIN_ELIGIBLE) { pool = dated; usedFallbackPool = true; }
const ranked = pool
.map(v => ({ ...v, views_per_day: v.view_count / Math.max(v.age_days, AGE_FLOOR_DAYS) }))
.sort((a, b) => b.views_per_day - a.views_per_day || b.view_count - a.view_count);
const medianViews = median(ranked.map(v => v.view_count).sort((a, b) => a - b));
const medianVpd = median(ranked.map(v => v.views_per_day).sort((a, b) => a - b));
const total = ranked.length;
ranked.forEach((v, i) => {
v.rank = i + 1;
v.rank_total = total;
v.percentile_from_top = total > 1 ? Math.round((i / (total - 1)) * 1000) / 10 : 0;
v.vs_median_views = medianViews ? v.view_count / medianViews : null;
v.vs_median_views_per_day = medianVpd ? v.views_per_day / medianVpd : null;
});
const mid = (total - 1) / 2;
const bandHalf = Math.max(1, Math.round(total * 0.15));
const inBand = ranked.filter((_, i) => Math.abs(i - mid) <= bandHalf);
const outOfBand = ranked.filter((_, i) => Math.abs(i - mid) > bandHalf).sort((a, b) => Math.abs(a.rank - 1 - mid) - Math.abs(b.rank - 1 - mid));
return {
stats: {
eligible: total,
excluded: videos.length - total,
median_views: medianViews,
median_views_per_day: medianVpd,
mean_views: total ? ranked.reduce((s, v) => s + v.view_count, 0) / total : null,
used_fallback_pool: usedFallbackPool,
min_age_days: MIN_AGE_DAYS,
age_floor_days: AGE_FLOOR_DAYS
},
best: ranked,
worst: [...ranked].reverse(),
median: [...shuffle(inBand, rng), ...outOfBand]
};
}
Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.