Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 21x 21x 17x 1x 1x 11x 11x 6x 11x 9x 9x 9x 9x 9x 11x 11x 11x 8x 8x 8x 8x 8x 8x 4x 4x 1x 1x 4x 8x 1x 3x | /**
* Selects and downloads the best representative image for a repository node.
* Priority: explicit `src/assets/imgs/project-image.png` probe → AST-derived
* candidate → first inline markdown image. Rewrites the README text so all
* badge-like and SVG image references point to the chosen local path.
*
* @param {object} node - Repository node with `name` and optionally `object.text`
* @param {string} mediaRoot - Absolute filesystem path where media folders are created
* @param {Function} getAxios - Factory returning an axios-like HTTP client
* @param {object} [opts] - Overrides: { parseReadme, isBadgeLike, mediaDownloader, readme, ast }
* @returns {Promise<string|null>} Filename relative to the repo media dir, or null
*/
async function processNodeMedia(node, mediaRoot, getAxios, opts = {}) {
const parseReadme = opts.parseReadme || require('../parseReadme');
const isBadgeLike = opts.isBadgeLike || (u => false);
const mediaDownloader = opts.mediaDownloader || require('../media/mediaDownloader');
const readme = opts.readme || (node.object && node.object.text) || '';
const ast = opts.ast || (readme ? (parseReadme.parseMarkdown ? parseReadme.parseMarkdown(readme) : null) : null);
try {
try { mediaDownloader.ensureDir(mediaRoot); } catch (e) {}
let candidate = null;
// Prefer explicit project-image path if available on main/master
const explicitPaths = [`https://raw.githubusercontent.com/keglev/${node.name}/main/src/assets/imgs/project-image.png`, `https://raw.githubusercontent.com/keglev/${node.name}/master/src/assets/imgs/project-image.png`];
for (const p of explicitPaths) {
try {
const ax = getAxios(); if (!ax) continue;
const h = await ax.head(p, { maxRedirects: 5, timeout: 4000 });
const ct = (h.headers['content-type']||'').toLowerCase();
Eif (h.status === 200 && /^image\//.test(ct)) { candidate = p; node._imageSelection = { original: 'src/assets/imgs/project-image.png', chosenUrl: p, reason: 'explicit-project-image' }; break; }
} catch (e) { /* ignore */ }
}
if (!candidate) candidate = (parseReadme.findImageCandidateFromAst && typeof parseReadme.findImageCandidateFromAst === 'function') ? parseReadme.findImageCandidateFromAst(ast) : null;
if (!candidate) {
const re = /!\[[^\]]*\]\(([^)]+)\)/g; const m = re.exec(readme); if (m) candidate = m && m[1] ? m[1].trim() : null;
}
if (!candidate) return null;
let img = candidate;
const sp = img.indexOf(' '); if (sp !== -1 && !img.startsWith('<')) img = img.slice(0, sp);
if (img.startsWith('<') && img.endsWith('>')) img = img.slice(1, -1);
const absoluteCandidates = /^https?:\/\//i.test(img) ? [img] : [ `https://raw.githubusercontent.com/keglev/${node.name}/main/${img.replace(/^\.\/?/, '')}`, `https://raw.githubusercontent.com/keglev/${node.name}/master/${img.replace(/^\.\/?/, '')}` ];
for (const u of absoluteCandidates) {
try {
const fn = await mediaDownloader.downloadIfNeeded(node.name, u, { originalCandidate: candidate });
if (fn) {
try { node._imageSelection = node._imageSelection || {}; node._imageSelection.chosenUrl = u; node._imageSelection.filename = fn; node._imageSelection.reason = node._imageSelection.reason || 'downloaded'; } catch (e) {}
node.primaryImage = `/projects_media/${node.name}/${fn}`;
try { node.object.text = node.object.text.split(candidate).join(node.primaryImage); } catch (e) {}
// Rewrite badge and SVG image refs so the UI always renders the downloaded raster image
try {
const pi = node.primaryImage;
Eif (pi && typeof node.object.text === 'string') {
node.object.text = node.object.text.replace(/!\[[^\]]*\]\((https?:\/\/[^)]+\.(svg))\)/gi, `;
node.object.text = node.object.text.replace(/!\[[^\]]*\]\((https?:\/\/[^)]+)\)/gi, (m, p1) => {
Iif (isBadgeLike(p1) || /\.svg$/i.test(p1)) return ``;
return m;
});
node.object.text = node.object.text.replace(/<img[^>]+src=["']([^"']+)["'][^>]*>/gi, (m, src) => {
if (isBadgeLike(src) || /\.svg$/i.test(src)) return `<img src="${pi}" />`;
return m;
});
}
} catch (e) {}
return fn;
}
} catch (e) { /* ignore and try next candidate */ }
}
} catch (e) { if (process.env.DEBUG_FETCH === '1' || process.env.DEBUG_FETCH === 'true') console.log('processNodeMedia error', e && e.message); }
return null;
}
module.exports = { processNodeMedia };
|