← back to Sdcc Images

scripts/refresh.js

118 lines

#!/usr/bin/env node
/**
 * Refresh data/files.json by querying Steve's personal Drive (via George tailnet).
 *  - Find every file or folder whose name contains 'sdcc' (case-insensitive)
 *  - Recurse into matching folders, pull all image files
 *  - Write data/files.json: { generatedAt, query, files: [{id, name, webViewLink, thumbnailUrl, parentName}] }
 */
const http = require('http');
const fs = require('fs');
const path = require('path');

const GEORGE = '100.107.67.67';
const PORT = 9850;
const ACCOUNT = 'steve-personal';
const AUTH = 'Basic ' + Buffer.from('admin:DWSecure2024!').toString('base64');

function get(path) {
  return new Promise((resolve, reject) => {
    const req = http.request(
      { hostname: GEORGE, port: PORT, method: 'GET', path, headers: { 'Authorization': AUTH } },
      res => { let c = ''; res.on('data', d => c += d); res.on('end', () => { try { resolve(JSON.parse(c)); } catch { resolve({ raw: c }); } }); }
    );
    req.on('error', reject);
    req.setTimeout(45000, () => { req.destroy(); reject(new Error('timeout')); });
    req.end();
  });
}

const enc = s => encodeURIComponent(s);

async function listAll(query) {
  const all = [];
  let pageSize = 200;
  // George doesn't support pageToken — single page up to 200. We'll do multiple narrow queries instead.
  const url = `/api/drive/list?account=${ACCOUNT}&pageSize=${pageSize}&q=${enc(query)}`;
  const r = await get(url);
  if (r.error) throw new Error('George error: ' + r.error);
  return r.files || [];
}

const FOLDER_MIME = 'application/vnd.google-apps.folder';
const IMAGE_MIMES = ['image/jpeg','image/jpg','image/png','image/gif','image/webp','image/heic','image/heif','image/vnd.adobe.photoshop','image/tiff'];

// Extra folders to recurse even if their name doesn't contain 'sdcc'.
// Add { id, label } here to include any shared Drive folder.
const EXTRA_FOLDERS = [
  { id: '0ByWfbpWMJPXkM0xrTFVadmVvSlU', label: 'SDCC (shared)' },
];

async function refresh() {
  console.log('Finding all SDCC-named folders + image files in Drive...');
  // 1. Top-level matches: anything with "sdcc" in name
  const topMatches = await listAll(`name contains 'sdcc' and trashed=false`);
  console.log(`top-level sdcc-named: ${topMatches.length}`);

  const folders = topMatches.filter(f => f.mimeType === FOLDER_MIME);
  const directImages = topMatches.filter(f => f.mimeType && f.mimeType.startsWith('image/'));
  console.log(`  folders: ${folders.length}`);
  console.log(`  direct images: ${directImages.length}`);

  // 2. Recurse into folders — list children of each folder
  // Drive query: 'PARENT_ID' in parents
  const collected = new Map();
  for (const img of directImages) collected.set(img.id, { ...img, parentName: '(root match)' });

  async function recurse(folderId, folderName, depth) {
    if (depth > 4) return; // safety
    let q = `'${folderId}' in parents and trashed=false`;
    const items = await listAll(q);
    for (const item of items) {
      if (item.mimeType === FOLDER_MIME) {
        await recurse(item.id, `${folderName} / ${item.name}`, depth + 1);
      } else if (item.mimeType && item.mimeType.startsWith('image/')) {
        if (!collected.has(item.id)) collected.set(item.id, { ...item, parentName: folderName });
      }
    }
  }

  for (const folder of folders) {
    console.log(`  recursing: ${folder.name}`);
    await recurse(folder.id, folder.name, 0);
    console.log(`    running total: ${collected.size}`);
  }

  for (const extra of EXTRA_FOLDERS) {
    console.log(`  recursing extra: ${extra.label} (${extra.id})`);
    await recurse(extra.id, extra.label, 0);
    console.log(`    running total: ${collected.size}`);
  }

  // 3. Build output
  const files = [...collected.values()].map(f => ({
    id: f.id,
    name: f.name,
    mimeType: f.mimeType,
    size: f.size ? parseInt(f.size) : null,
    modifiedTime: f.modifiedTime,
    parentName: f.parentName,
    webViewLink: f.webViewLink || `https://drive.google.com/file/d/${f.id}/view`,
    thumbnailLink: `https://drive.google.com/thumbnail?id=${f.id}&sz=w400`,
  })).sort((a, b) => (a.parentName + a.name).localeCompare(b.parentName + b.name));

  const out = {
    generatedAt: new Date().toISOString(),
    query: "name contains 'sdcc' (recursive)",
    account: ACCOUNT,
    fileCount: files.length,
    files,
  };

  const outPath = path.join(__dirname, '..', 'data', 'files.json');
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
  fs.writeFileSync(outPath, JSON.stringify(out, null, 2));
  console.log(`\nDONE — ${files.length} images written to ${outPath}`);
}

refresh().catch(e => { console.error('FATAL', e.message); process.exit(1); });