// ── Viral Hunter · Google Sheets backend ──────────────────────
// Paste this entire file into Apps Script, then deploy as a Web App:
// Execute as: Me | Who has access: Anyone
// ──────────────────────────────────────────────────────────────
const SHEET_NAME = 'Lists';
function doGet(e) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName(SHEET_NAME);
const cb = e.parameter.callback || null; // JSONP support
// Create the sheet with headers on first run
if (!sheet) {
sheet = ss.insertSheet(SHEET_NAME);
sheet.appendRow(['Name', 'Channels', 'Updated']);
sheet.setFrozenRows(1);
}
const action = (e.parameter.action || 'getLists');
// ── GET ALL LISTS ──────────────────────────────────────────
if (action === 'getLists') {
const data = sheet.getDataRange().getValues();
const lists = [];
for (let i = 1; i < data.length; i++) {
if (data[i][0]) {
lists.push({ name: data[i][0], channels: data[i][1] });
}
}
return respond({ ok: true, lists: lists }, cb);
}
// ── SAVE / UPDATE A LIST ───────────────────────────────────
if (action === 'saveList') {
const name = e.parameter.name || '';
const channels = e.parameter.channels || '';
if (!name) return respond({ ok: false, error: 'Missing name' }, cb);
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] === name) {
sheet.getRange(i + 1, 2, 1, 2).setValues([[channels, new Date().toISOString()]]);
return respond({ ok: true }, cb);
}
}
sheet.appendRow([name, channels, new Date().toISOString()]);
return respond({ ok: true }, cb);
}
// ── DELETE A LIST ──────────────────────────────────────────
if (action === 'deleteList') {
const name = e.parameter.name || '';
if (!name) return respond({ ok: false, error: 'Missing name' }, cb);
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] === name) {
sheet.deleteRow(i + 1);
return respond({ ok: true }, cb);
}
}
return respond({ ok: false, error: 'List not found' }, cb);
}
return respond({ ok: false, error: 'Unknown action: ' + action }, cb);
}
// Helper: returns JSON or JSONP depending on whether callback is present
function respond(obj, callback) {
const json = JSON.stringify(obj);
if (callback) {
return ContentService
.createTextOutput(callback + '(' + json + ')')
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
return ContentService
.createTextOutput(json)
.setMimeType(ContentService.MimeType.JSON);
}
Free key from Google Cloud Console → APIs & Services → Enable YouTube Data API v3 → Credentials → Create API key.
Accepts /@handle, /channel/UCxxx, and /user/name formats.
Videos under 3 minutes or tagged #shorts are automatically excluded.