fitness: add ExerciseDB v2 scrape data, media, and ID mapping
Scrape scripts for ExerciseDB v2 API (scrape-exercises.ts, download-exercise-media.ts), raw data for 200 exercises with images/videos, and a 1:1 mapping from ExerciseDB IDs to internal kebab-case slugs (exercisedb-map.ts). 23 exercises matched to existing internal IDs, 177 new slugs generated.
@@ -32,3 +32,6 @@ OLLAMA_URL="http://localhost:11434" # Local Ollama server URL
|
|||||||
|
|
||||||
# HuggingFace Transformers Model Cache (for nutrition embedding models)
|
# HuggingFace Transformers Model Cache (for nutrition embedding models)
|
||||||
TRANSFORMERS_CACHE="/var/cache/transformers" # Must be writable by build and runtime user
|
TRANSFORMERS_CACHE="/var/cache/transformers" # Must be writable by build and runtime user
|
||||||
|
|
||||||
|
# ExerciseDB v2 API (RapidAPI) - for scraping exercise data
|
||||||
|
RAPIDAPI_KEY="your-rapidapi-key"
|
||||||
|
|||||||
117
scripts/download-exercise-media.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* Downloads all exercise images and videos from the ExerciseDB CDN.
|
||||||
|
*
|
||||||
|
* Run with: pnpm exec vite-node scripts/download-exercise-media.ts
|
||||||
|
*
|
||||||
|
* Reads: src/lib/data/exercisedb-raw.json
|
||||||
|
* Outputs: static/fitness/exercises/<exerciseId>/
|
||||||
|
* - images: 360p.jpg, 480p.jpg, 720p.jpg, 1080p.jpg
|
||||||
|
* - video: video.mp4
|
||||||
|
*
|
||||||
|
* Resumes automatically — skips files that already exist on disk.
|
||||||
|
*/
|
||||||
|
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
|
||||||
|
import { resolve, extname } from 'path';
|
||||||
|
|
||||||
|
const RAW_PATH = resolve('src/lib/data/exercisedb-raw.json');
|
||||||
|
const OUT_DIR = resolve('static/fitness/exercises');
|
||||||
|
const CONCURRENCY = 10;
|
||||||
|
|
||||||
|
interface DownloadTask {
|
||||||
|
url: string;
|
||||||
|
dest: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms: number) {
|
||||||
|
return new Promise(r => setTimeout(r, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function download(url: string, dest: string, retries = 3): Promise<boolean> {
|
||||||
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
|
||||||
|
const buf = Buffer.from(await res.arrayBuffer());
|
||||||
|
writeFileSync(dest, buf);
|
||||||
|
return true;
|
||||||
|
} catch (err: any) {
|
||||||
|
if (attempt === retries) {
|
||||||
|
console.error(` FAILED ${url}: ${err.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
await sleep(1000 * attempt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runQueue(tasks: DownloadTask[]) {
|
||||||
|
let done = 0;
|
||||||
|
let failed = 0;
|
||||||
|
const total = tasks.length;
|
||||||
|
|
||||||
|
async function worker() {
|
||||||
|
while (tasks.length > 0) {
|
||||||
|
const task = tasks.shift()!;
|
||||||
|
const ok = await download(task.url, task.dest);
|
||||||
|
if (!ok) failed++;
|
||||||
|
done++;
|
||||||
|
if (done % 50 === 0 || done === total) {
|
||||||
|
console.log(` ${done}/${total} downloaded${failed ? ` (${failed} failed)` : ''}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const workers = Array.from({ length: CONCURRENCY }, () => worker());
|
||||||
|
await Promise.all(workers);
|
||||||
|
return { done, failed };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('=== Exercise Media Downloader ===\n');
|
||||||
|
|
||||||
|
if (!existsSync(RAW_PATH)) {
|
||||||
|
console.error(`Missing ${RAW_PATH} — run scrape-exercises.ts first`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = JSON.parse(readFileSync(RAW_PATH, 'utf-8'));
|
||||||
|
const exercises: any[] = data.exercises;
|
||||||
|
console.log(`${exercises.length} exercises in raw data\n`);
|
||||||
|
|
||||||
|
const tasks: DownloadTask[] = [];
|
||||||
|
|
||||||
|
for (const ex of exercises) {
|
||||||
|
const dir = resolve(OUT_DIR, ex.exerciseId);
|
||||||
|
mkdirSync(dir, { recursive: true });
|
||||||
|
|
||||||
|
// Multi-resolution images
|
||||||
|
if (ex.imageUrls) {
|
||||||
|
for (const [res, url] of Object.entries(ex.imageUrls as Record<string, string>)) {
|
||||||
|
const ext = extname(new URL(url).pathname) || '.jpg';
|
||||||
|
const dest = resolve(dir, `${res}${ext}`);
|
||||||
|
if (!existsSync(dest)) tasks.push({ url, dest });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Video
|
||||||
|
if (ex.videoUrl) {
|
||||||
|
const dest = resolve(dir, 'video.mp4');
|
||||||
|
if (!existsSync(dest)) tasks.push({ url: ex.videoUrl, dest });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tasks.length === 0) {
|
||||||
|
console.log('All media already downloaded!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${tasks.length} files to download (skipping existing)\n`);
|
||||||
|
const { done, failed } = await runQueue(tasks);
|
||||||
|
console.log(`\nDone! ${done - failed} downloaded, ${failed} failed.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
156
scripts/scrape-exercises.ts
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
* Scrapes the full ExerciseDB v2 API (via RapidAPI) and saves raw data.
|
||||||
|
*
|
||||||
|
* Run with: RAPIDAPI_KEY=... pnpm exec vite-node scripts/scrape-exercises.ts
|
||||||
|
*
|
||||||
|
* Outputs: src/lib/data/exercisedb-raw.json
|
||||||
|
*
|
||||||
|
* Supports resuming — already-fetched exercises are read from the output file
|
||||||
|
* and skipped. Saves to disk after every detail fetch.
|
||||||
|
*/
|
||||||
|
import { writeFileSync, readFileSync, existsSync } from 'fs';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
const API_HOST = 'edb-with-videos-and-images-by-ascendapi.p.rapidapi.com';
|
||||||
|
const API_KEY = process.env.RAPIDAPI_KEY;
|
||||||
|
if (!API_KEY) {
|
||||||
|
console.error('Set RAPIDAPI_KEY environment variable');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE = `https://${API_HOST}/api/v1`;
|
||||||
|
const HEADERS = {
|
||||||
|
'x-rapidapi-host': API_HOST,
|
||||||
|
'x-rapidapi-key': API_KEY,
|
||||||
|
};
|
||||||
|
|
||||||
|
const OUTPUT_PATH = resolve('src/lib/data/exercisedb-raw.json');
|
||||||
|
const IDS_CACHE_PATH = resolve('src/lib/data/.exercisedb-ids.json');
|
||||||
|
const DELAY_MS = 1500;
|
||||||
|
const MAX_RETRIES = 5;
|
||||||
|
|
||||||
|
function sleep(ms: number) {
|
||||||
|
return new Promise(r => setTimeout(r, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function apiFetch(path: string, attempt = 1): Promise<any> {
|
||||||
|
const res = await fetch(`${BASE}${path}`, { headers: HEADERS });
|
||||||
|
if (res.status === 429 && attempt <= MAX_RETRIES) {
|
||||||
|
const wait = DELAY_MS * 2 ** attempt;
|
||||||
|
console.warn(` rate limited on ${path}, retrying in ${wait}ms...`);
|
||||||
|
await sleep(wait);
|
||||||
|
return apiFetch(path, attempt + 1);
|
||||||
|
}
|
||||||
|
if (!res.ok) throw new Error(`${res.status} ${res.statusText} for ${path}`);
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadExisting(): { metadata: any; exercises: any[] } | null {
|
||||||
|
if (!existsSync(OUTPUT_PATH)) return null;
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(readFileSync(OUTPUT_PATH, 'utf-8'));
|
||||||
|
if (data.exercises?.length) {
|
||||||
|
console.log(` found existing file with ${data.exercises.length} exercises`);
|
||||||
|
return { metadata: data.metadata, exercises: data.exercises };
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveToDisk(metadata: any, exercises: any[]) {
|
||||||
|
const output = {
|
||||||
|
scrapedAt: new Date().toISOString(),
|
||||||
|
metadata,
|
||||||
|
exercises,
|
||||||
|
};
|
||||||
|
writeFileSync(OUTPUT_PATH, JSON.stringify(output, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAllIds(): Promise<string[]> {
|
||||||
|
const ids: string[] = [];
|
||||||
|
let cursor: string | undefined;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const params = new URLSearchParams({ limit: '100' });
|
||||||
|
if (cursor) params.set('after', cursor);
|
||||||
|
|
||||||
|
const res = await apiFetch(`/exercises?${params}`);
|
||||||
|
for (const ex of res.data) {
|
||||||
|
ids.push(ex.exerciseId);
|
||||||
|
}
|
||||||
|
console.log(` fetched page, ${ids.length} IDs so far`);
|
||||||
|
|
||||||
|
if (!res.meta.hasNextPage) break;
|
||||||
|
cursor = res.meta.nextCursor;
|
||||||
|
await sleep(DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchMetadata() {
|
||||||
|
const endpoints = ['/bodyparts', '/equipments', '/muscles', '/exercisetypes'] as const;
|
||||||
|
const keys = ['bodyParts', 'equipments', 'muscles', 'exerciseTypes'] as const;
|
||||||
|
const result: Record<string, any> = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < endpoints.length; i++) {
|
||||||
|
const res = await apiFetch(endpoints[i]);
|
||||||
|
result[keys[i]] = res.data;
|
||||||
|
await sleep(DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('=== ExerciseDB v2 Scraper ===\n');
|
||||||
|
|
||||||
|
const existing = loadExisting();
|
||||||
|
const fetchedIds = new Set(existing?.exercises.map((e: any) => e.exerciseId) ?? []);
|
||||||
|
|
||||||
|
console.log('Fetching metadata...');
|
||||||
|
const metadata = existing?.metadata ?? await fetchMetadata();
|
||||||
|
if (!existing?.metadata) {
|
||||||
|
console.log(` ${metadata.bodyParts.length} body parts, ${metadata.equipments.length} equipments, ${metadata.muscles.length} muscles, ${metadata.exerciseTypes.length} exercise types\n`);
|
||||||
|
} else {
|
||||||
|
console.log(' using cached metadata\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
let ids: string[];
|
||||||
|
if (existsSync(IDS_CACHE_PATH)) {
|
||||||
|
ids = JSON.parse(readFileSync(IDS_CACHE_PATH, 'utf-8'));
|
||||||
|
console.log(`Using cached exercise IDs (${ids.length})\n`);
|
||||||
|
} else {
|
||||||
|
console.log('Fetching exercise IDs...');
|
||||||
|
ids = await fetchAllIds();
|
||||||
|
writeFileSync(IDS_CACHE_PATH, JSON.stringify(ids));
|
||||||
|
console.log(` ${ids.length} total exercises\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remaining = ids.filter(id => !fetchedIds.has(id));
|
||||||
|
if (remaining.length === 0) {
|
||||||
|
console.log('All exercises already fetched!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`Fetching ${remaining.length} remaining details (${fetchedIds.size} already cached)...`);
|
||||||
|
|
||||||
|
const exercises = [...(existing?.exercises ?? [])];
|
||||||
|
|
||||||
|
for (const id of remaining) {
|
||||||
|
const detail = await apiFetch(`/exercises/${id}`);
|
||||||
|
exercises.push(detail.data);
|
||||||
|
saveToDisk(metadata, exercises);
|
||||||
|
|
||||||
|
if (exercises.length % 10 === 0 || exercises.length === ids.length) {
|
||||||
|
console.log(` ${exercises.length}/${ids.length} details fetched`);
|
||||||
|
}
|
||||||
|
await sleep(DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\nDone! ${exercises.length} exercises written to ${OUTPUT_PATH}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
1
src/lib/data/.exercisedb-ids.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
["exr_41n2ha5iPFpN3hEJ", "exr_41n2haAabPyN5t8y", "exr_41n2hadPLLFRGvFk", "exr_41n2hadQgEEX8wDN", "exr_41n2haNJ3NA8yCE2", "exr_41n2havo95Y2QpkW", "exr_41n2hbdZww1thMKz", "exr_41n2hbLX4XH8xgN7", "exr_41n2hbYPY4jLKxW3", "exr_41n2hc2VrB8ofxrW", "exr_41n2hcFJpBvAkXCP", "exr_41n2hcm5HH6H684G", "exr_41n2hcw2FN534HcA", "exr_41n2hd6SThQhAdnZ", "exr_41n2hd78zujKUEWK", "exr_41n2hdCBvmbCPaVE", "exr_41n2hdHtZrMPkcqY", "exr_41n2hdkBpqwoDmVq", "exr_41n2hdo2vCtq4F3E", "exr_41n2hdsGcuzs4WrV", "exr_41n2hdWu3oaCGdWT", "exr_41n2he2doZNpmXkX", "exr_41n2hek6i3exMARx", "exr_41n2hezAZ6CdkAcM", "exr_41n2hfa11fPnk8y9", "exr_41n2hfnnXz9shkBi", "exr_41n2hftBVLiXgtRQ", "exr_41n2hfYD2sH4TRCH", "exr_41n2hG9pRT55cGVk", "exr_41n2hGbCptD8Nosk", "exr_41n2hgCHNgtVLHna", "exr_41n2hGD4omjWVnbS", "exr_41n2hGioS8HumEF7", "exr_41n2hGNrmUnF58Yy", "exr_41n2hGRSg9WCoTYT", "exr_41n2hGUso7JFmuYR", "exr_41n2hGy6zE7fN6v2", "exr_41n2hH6VGNz6cNtv", "exr_41n2hhBHuvSdAeCJ", "exr_41n2hHCXQpZYhxhc", "exr_41n2hHdjQpnyNdie", "exr_41n2hHH9bNfi98YU", "exr_41n2hhiWL8njJDZe", "exr_41n2hHLE8aJXaxKR", "exr_41n2hHRszDHarrxK", "exr_41n2hhumxqyAFuTb", "exr_41n2hJ5Harig7K7F", "exr_41n2hJFwC7ocdiNm", "exr_41n2hjkBReJMbDJk", "exr_41n2hjuGpcex14w7", "exr_41n2hk3YSCjnZ9um", "exr_41n2hkB3FeGM3DEL", "exr_41n2hkCHzg1AXdkV", "exr_41n2hKiaWSZQTqgE", "exr_41n2hkK8hGAcSnW7", "exr_41n2hkknYAEEE3tc", "exr_41n2hkmMrSwcHkZ8", "exr_41n2hKoQnnSRPZrE", "exr_41n2hKZmyYXB2UL4", "exr_41n2hLA8xydD4dzE", "exr_41n2hLpyk6MsV85U", "exr_41n2hLUqpev5gSzJ", "exr_41n2hLx2rvhz95GC", "exr_41n2hLZZAH2F2UkS", "exr_41n2hM8vgMA6MREd", "exr_41n2hmbfYcYtedgz", "exr_41n2hmFcGGUCS289", "exr_41n2hmGR8WuVfe1U", "exr_41n2hmhb4jD7H8Qk", "exr_41n2hmhxk35fbHbC", "exr_41n2hMRXm49mM62z", "exr_41n2hmvGdVRvvnNY", "exr_41n2hMydkzFvswVX", "exr_41n2hMZCmZBvQApL", "exr_41n2hn2kPMag9WCf", "exr_41n2hN468sP27Sac", "exr_41n2hn8rpbYihzEW", "exr_41n2hnAGfMhp95LQ", "exr_41n2hnbt5GwwY7gr", "exr_41n2hNCTCWtWAqzH", "exr_41n2hndkoGHD1ogh", "exr_41n2hNfaYkEKLQHK", "exr_41n2hnFD2bT6sruf", "exr_41n2hNifNwh2tbR2", "exr_41n2hNjcmNgtPJ1H", "exr_41n2hnougzKKhhqu", "exr_41n2hNRM1dGGhGYL", "exr_41n2hnx1hnDdketU", "exr_41n2hNXJadYcfjnd", "exr_41n2hoFGGwZDNFT1", "exr_41n2hoifHqpb7WK9", "exr_41n2homrPqqs8coG", "exr_41n2howQHvcrcrW6", "exr_41n2hoyHUrhBiEWg", "exr_41n2hozyXuCmDTdZ", "exr_41n2hpDWoTxocW8G", "exr_41n2hpeHAizgtrEw", "exr_41n2hPgRbN1KtJuD", "exr_41n2hpJxS5VQKtBL", "exr_41n2hpLLs1uU5atr", "exr_41n2hpnZ6oASM662", "exr_41n2hPRWorCfCPov", "exr_41n2hpTMDhTxYkvi", "exr_41n2hPvwqp7Pwvks", "exr_41n2hPxDaq9kFjiL", "exr_41n2hq3Wm6ANkgUz", "exr_41n2hQcqPQ37Dmxj", "exr_41n2hQDiSwTZXM4F", "exr_41n2hQeNyCnt3uFh", "exr_41n2hQEqKxuAfV1D", "exr_41n2hqfZb8UHBvB9", "exr_41n2hQHmRSoUkk9F", "exr_41n2hqjVS3nwBoyr", "exr_41n2hQp1pR7heQq9", "exr_41n2hQtaWxPLNFwX", "exr_41n2hqw5LsDpeE2i", "exr_41n2hqYdxG87hXz1", "exr_41n2hR12rPqdpWP8", "exr_41n2hRaLxY7YfNbg", "exr_41n2hRH4aTB379Tp", "exr_41n2hrHSqBnVWRRB", "exr_41n2hRicz5MdZEns", "exr_41n2hrN2RCZBZU9h", "exr_41n2hrSQZRD4yG7P", "exr_41n2hRZ6fgsLyd77", "exr_41n2hS5UrMusVCEE", "exr_41n2hs6camM22yBG", "exr_41n2hsBtDXapcADg", "exr_41n2hSF5U97sFAr8", "exr_41n2hSGs9Q3NVGhs", "exr_41n2hSkc2tRLxVVS", "exr_41n2hskeb9dXgBoC", "exr_41n2hSMjcavNjk3c", "exr_41n2hSq88Ni3KCny", "exr_41n2hsSnmS946i2k", "exr_41n2hSvEPVntpxSG", "exr_41n2hsVHu7B1MTdr", "exr_41n2hSxsNAV8tGS6", "exr_41n2hsZWJA1ujZUd", "exr_41n2hTaeNKWhMQHH", "exr_41n2hTCBiQVsEfZ7", "exr_41n2hTkfLWpc57BQ", "exr_41n2hTs4q3ihihZs", "exr_41n2htTnk4CuspZh", "exr_41n2htzPyjcc3Mt2", "exr_41n2hU3XPwUFSpkC", "exr_41n2hU4y6EaYXFhr", "exr_41n2hu5r8WMaLUkH", "exr_41n2hUBVSgXaKhau", "exr_41n2huc12BsuDNYQ", "exr_41n2hUDuvCas2EB3", "exr_41n2huf7mAC2rhfC", "exr_41n2hUKc7JPrtJQj", "exr_41n2hupxPcdnktBC", "exr_41n2huQw1pHKH9cw", "exr_41n2hushK9NGVfyK", "exr_41n2hUVNhvcS73Dt", "exr_41n2huXeEFSaqo4G", "exr_41n2hVCJfpAvJcdU", "exr_41n2hvg2FRT5XMyJ", "exr_41n2hvjrFJ2KjzGm", "exr_41n2hvrsUaWWb9Mk", "exr_41n2hvzxocyjoGgL", "exr_41n2hw1QspZ6uXoW", "exr_41n2hw4iksLYXESz", "exr_41n2hw8nSYiaCXW1", "exr_41n2hW9gDXAJJMmH", "exr_41n2hWbP5uF6PQpU", "exr_41n2hWgAAtQeA3Lh", "exr_41n2hwio5ECAfLuS", "exr_41n2hwoc6PkW1UJJ", "exr_41n2hWQVZanCB1d7", "exr_41n2hWVVEwU54UtF", "exr_41n2hWxnJoGwbJpa", "exr_41n2hx6oyEujP1B6", "exr_41n2hx9wyaRGNyvs", "exr_41n2hXfpvSshoXWG", "exr_41n2hxg75dFGERdp", "exr_41n2hxnFMotsXTj3", "exr_41n2hxqpSU5p6DZv", "exr_41n2hXQw5yAbbXL8", "exr_41n2hXszY7TgwKy4", "exr_41n2hXvPyEyMBgNR", "exr_41n2hxxePSdr5oN1", "exr_41n2hXXpvbykPY3q", "exr_41n2hXYRxFHnQAD4", "exr_41n2hy8pKXtzuBh8", "exr_41n2hY9EdwkdGz9a", "exr_41n2hYAP9oGEZk2P", "exr_41n2hynD9srC1kY7", "exr_41n2hyNf5GebszTf", "exr_41n2hyWsNxNYWpk3", "exr_41n2hYWXejezzLjv", "exr_41n2hYXWwoxiUk57", "exr_41n2hZ7uoN5JnUJY", "exr_41n2hzAMXkkQQ5T2", "exr_41n2hzfRXQDaLYJh", "exr_41n2hZqkvM55qJve", "exr_41n2hZqwsLkCVnzr", "exr_41n2hzZBVbWFoLK3"]
|
||||||
219
src/lib/data/exercisedb-map.ts
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
/**
|
||||||
|
* 1:1 mapping from ExerciseDB v2 exercise IDs to internal kebab-case slugs.
|
||||||
|
*
|
||||||
|
* - Entries that match an existing exercise in exercises.ts reuse that ID.
|
||||||
|
* - All other entries get a new slug derived from the ExerciseDB name.
|
||||||
|
*
|
||||||
|
* Regenerate with: scripts/scrape-exercises.ts (source data)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const exerciseDbMap: Record<string, string> = {
|
||||||
|
// ── Matched to existing internal exercises ────────────────────────
|
||||||
|
'exr_41n2hxnFMotsXTj3': 'bench-press-barbell', // Bench Press (BARBELL)
|
||||||
|
'exr_41n2hpLLs1uU5atr': 'bulgarian-split-squat-dumbbell', // Bulgarian Split Squat
|
||||||
|
'exr_41n2hSGs9Q3NVGhs': 'calf-raise-standing', // Bodyweight Standing Calf Raise
|
||||||
|
'exr_41n2hkK8hGAcSnW7': 'chest-dip', // Chest Dip
|
||||||
|
'exr_41n2hXszY7TgwKy4': 'chin-up', // Chin-Up
|
||||||
|
'exr_41n2hskeb9dXgBoC': 'crunch', // Crunch Floor
|
||||||
|
'exr_41n2hY9EdwkdGz9a': 'dumbbell-row', // Dumbbell One Arm Bent-over Row
|
||||||
|
'exr_41n2howQHvcrcrW6': 'front-raise-dumbbell', // Front Raise (DUMBBELL)
|
||||||
|
'exr_41n2hQDiSwTZXM4F': 'goblet-squat-dumbbell', // Goblet Squat (DUMBBELL)
|
||||||
|
'exr_41n2hxxePSdr5oN1': 'hip-thrust-barbell', // Hip Thrusts
|
||||||
|
'exr_41n2hfYD2sH4TRCH': 'hyperextension', // Hyperextension
|
||||||
|
'exr_41n2hN468sP27Sac': 'jump-rope', // Jump Rope (ROPE)
|
||||||
|
'exr_41n2hjuGpcex14w7': 'lateral-raise-dumbbell', // Lateral Raise (DUMBBELL)
|
||||||
|
'exr_41n2hYXWwoxiUk57': 'lunge-dumbbell', // Lunge
|
||||||
|
'exr_41n2hs6camM22yBG': 'overhead-press-dumbbell', // Seated Shoulder Press (DUMBBELL)
|
||||||
|
'exr_41n2hXQw5yAbbXL8': 'plank', // Front Plank
|
||||||
|
'exr_41n2hsBtDXapcADg': 'pull-up', // Pull-up
|
||||||
|
'exr_41n2hNXJadYcfjnd': 'push-up', // Push-up
|
||||||
|
'exr_41n2hyNf5GebszTf': 'reverse-fly-dumbbell', // Dumbbell Rear Delt Fly
|
||||||
|
'exr_41n2hn8rpbYihzEW': 'romanian-deadlift-dumbbell', // Romanian Deadlift (DUMBBELL)
|
||||||
|
'exr_41n2hWVVEwU54UtF': 'russian-twist', // Russian Twist
|
||||||
|
'exr_41n2hdHtZrMPkcqY': 'skullcrusher-dumbbell', // Dumbbell Lying Floor Skull Crusher
|
||||||
|
'exr_41n2hadQgEEX8wDN': 'tricep-dip', // Triceps Dip
|
||||||
|
|
||||||
|
// ── New slugs (no existing internal match) ───────────────────────
|
||||||
|
'exr_41n2hRH4aTB379Tp': '45-degree-hyperextension', // 45 degree hyperextension
|
||||||
|
'exr_41n2hqfZb8UHBvB9': 'alternate-lying-floor-leg-raise', // Alternate Lying Floor Leg Raise
|
||||||
|
'exr_41n2hezAZ6CdkAcM': 'alternate-punching', // Alternate Punching
|
||||||
|
'exr_41n2hNifNwh2tbR2': 'ankle-dorsal-flexion-articulations', // Ankle - Dorsal Flexion - Articulations
|
||||||
|
'exr_41n2he2doZNpmXkX': 'ankle-plantar-flexion-articulations', // Ankle - Plantar Flexion - Articulations
|
||||||
|
'exr_41n2hMRXm49mM62z': 'arnold-press', // Arnold Press (DUMBBELL)
|
||||||
|
'exr_41n2hmhb4jD7H8Qk': 'assault-bike-run', // Assault Bike Run (MACHINE)
|
||||||
|
'exr_41n2hQeNyCnt3uFh': 'back-pec-stretch', // Back Pec Stretch
|
||||||
|
'exr_41n2hwoc6PkW1UJJ': 'barbell-standing-calf-raise', // Barbell Standing Calf Raise
|
||||||
|
'exr_41n2hoFGGwZDNFT1': 'bench-dip-on-floor', // Bench dip on floor
|
||||||
|
'exr_41n2hxqpSU5p6DZv': 'biceps-leg-concentration-curl', // Biceps Leg Concentration Curl
|
||||||
|
'exr_41n2hTkfLWpc57BQ': 'body-up', // Body-Up
|
||||||
|
'exr_41n2hNjcmNgtPJ1H': 'bodyweight-rear-lunge', // Bodyweight Rear Lunge
|
||||||
|
'exr_41n2hrHSqBnVWRRB': 'bodyweight-single-leg-deadlift', // Bodyweight Single Leg Deadlift
|
||||||
|
'exr_41n2ha5iPFpN3hEJ': 'bridge-mountain-climber', // Bridge - Mountain Climber
|
||||||
|
'exr_41n2hmbfYcYtedgz': 'bridge-pose-setu-bandhasana', // Bridge Pose Setu Bandhasana
|
||||||
|
'exr_41n2hqYdxG87hXz1': 'burpee', // Burpee
|
||||||
|
'exr_41n2hZqwsLkCVnzr': 'butt-kicks', // Butt Kicks
|
||||||
|
'exr_41n2hnougzKKhhqu': 'butterfly-yoga-pose', // Butterfly Yoga Pose
|
||||||
|
'exr_41n2hn2kPMag9WCf': 'cable-seated-neck-extension', // Cable Seated Neck Extension (CABLE)
|
||||||
|
'exr_41n2hUDuvCas2EB3': 'cable-seated-neck-flexion', // Cable Seated Neck Flexion (CABLE)
|
||||||
|
'exr_41n2hcm5HH6H684G': 'calf-raise-from-deficit-chair', // Calf Raise from Deficit with Chair Supported
|
||||||
|
'exr_41n2hHCXQpZYhxhc': 'calf-stretch-wall', // Calf Stretch With Hands Against Wall
|
||||||
|
'exr_41n2hSkc2tRLxVVS': 'calf-stretch-rope', // Calf Stretch with Rope
|
||||||
|
'exr_41n2hG9pRT55cGVk': 'calves-stretch', // Calves stretch
|
||||||
|
'exr_41n2hGD4omjWVnbS': 'calves-stretch-standing', // Calves Stretch (variant)
|
||||||
|
'exr_41n2hd6SThQhAdnZ': 'chin-ups-neutral', // Chin-ups (variant)
|
||||||
|
'exr_41n2hWQVZanCB1d7': 'circles-arm', // Circles Arm
|
||||||
|
'exr_41n2hk3YSCjnZ9um': 'circles-knee-stretch', // Circles Knee Stretch
|
||||||
|
'exr_41n2hrSQZRD4yG7P': 'circles-knee-stretch-alt', // Circles Knee Stretch (variant)
|
||||||
|
'exr_41n2hkmMrSwcHkZ8': 'clap-push-up', // Clap Push Up
|
||||||
|
'exr_41n2hPgRbN1KtJuD': 'close-grip-push-up', // Close-grip Push-up
|
||||||
|
'exr_41n2hHLE8aJXaxKR': 'cobra-push-up', // Cobra Push-up
|
||||||
|
'exr_41n2hVCJfpAvJcdU': 'commando-pull-up', // Commando Pull-up
|
||||||
|
'exr_41n2hNCTCWtWAqzH': 'cow-stretch', // Cow Stretch
|
||||||
|
'exr_41n2hgCHNgtVLHna': 'cross-body-hammer-curl', // Cross Body Hammer Curl (DUMBBELL)
|
||||||
|
'exr_41n2hGUso7JFmuYR': 'decline-push-up', // Decline Push-Up
|
||||||
|
'exr_41n2hdCBvmbCPaVE': 'diamond-press', // Diamond Press
|
||||||
|
'exr_41n2hnbt5GwwY7gr': 'diamond-push-up', // Diamond Push up
|
||||||
|
'exr_41n2hRZ6fgsLyd77': 'diamond-push-up-alt', // Diamond Push-up (variant)
|
||||||
|
'exr_41n2hUKc7JPrtJQj': 'dip-on-floor-with-chair', // Dip on Floor with Chair
|
||||||
|
'exr_41n2hWbP5uF6PQpU': 'donkey-calf-raise', // Donkey Calf Raise
|
||||||
|
'exr_41n2hW9gDXAJJMmH': 'downward-facing-dog', // Downward Facing Dog
|
||||||
|
'exr_41n2hTaeNKWhMQHH': 'dumbbell-burpee', // Dumbbell burpee
|
||||||
|
'exr_41n2hXfpvSshoXWG': 'dumbbell-clean-and-press', // Dumbbell Clean and Press
|
||||||
|
'exr_41n2hZ7uoN5JnUJY': 'dumbbell-decline-one-arm-hammer-press', // Dumbbell Decline One Arm Hammer Press
|
||||||
|
'exr_41n2haNJ3NA8yCE2': 'dumbbell-incline-one-arm-hammer-press', // Dumbbell Incline One Arm Hammer Press
|
||||||
|
'exr_41n2huf7mAC2rhfC': 'dumbbell-jumping-squat', // Dumbbell Jumping Squat
|
||||||
|
'exr_41n2hbLX4XH8xgN7': 'dumbbell-single-leg-calf-raise', // Dumbbell Single Leg Calf Raise
|
||||||
|
'exr_41n2hQtaWxPLNFwX': 'dumbbell-standing-calf-raise', // Dumbbell Standing Calf Raise
|
||||||
|
'exr_41n2hTCBiQVsEfZ7': 'dumbbell-side-bend', // Dumbbell Side Bend
|
||||||
|
'exr_41n2hcw2FN534HcA': 'dumbbell-stiff-leg-deadlift', // Dumbbell Stiff Leg Deadlift
|
||||||
|
'exr_41n2hQp1pR7heQq9': 'dumbbell-stiff-leg-deadlift-alt', // Dumbbell Stiff Leg Deadlift (variant)
|
||||||
|
'exr_41n2hek6i3exMARx': 'elbow-flexion-articulations', // Elbow - Flexion - Articulations
|
||||||
|
'exr_41n2hqw5LsDpeE2i': 'elbow-flexor-stretch', // Elbow Flexor Stretch
|
||||||
|
'exr_41n2hy8pKXtzuBh8': 'elbow-up-and-down-dynamic-plank', // Elbow Up and Down Dynamic Plank
|
||||||
|
'exr_41n2hKZmyYXB2UL4': 'elbows-back-stretch', // Elbows Back Stretch
|
||||||
|
'exr_41n2hNfaYkEKLQHK': 'elevated-push-up', // Elevanted Push-Up
|
||||||
|
'exr_41n2hfa11fPnk8y9': 'elliptical-machine-walk', // Elliptical Machine Walk (MACHINE)
|
||||||
|
'exr_41n2hpTMDhTxYkvi': 'elliptical-machine-walk-alt', // Elliptical Machine Walk (variant)
|
||||||
|
'exr_41n2hH6VGNz6cNtv': 'extension-inclination-neck-stretch', // Extension And Inclination Neck Stretch
|
||||||
|
'exr_41n2hLx2rvhz95GC': 'feet-ankles-rotation-stretch', // Feet and Ankles Rotation Stretch
|
||||||
|
'exr_41n2hzZBVbWFoLK3': 'feet-ankles-side-to-side-stretch', // Feet and Ankles Side to Side Stretch
|
||||||
|
'exr_41n2havo95Y2QpkW': 'feet-ankles-stretch', // Feet and Ankles Stretch
|
||||||
|
'exr_41n2hnx1hnDdketU': 'feet-ankles-stretch-alt', // Feet and Ankles Stretch (variant)
|
||||||
|
'exr_41n2hPRWorCfCPov': 'flutter-kicks', // Flutter Kicks
|
||||||
|
'exr_41n2hvg2FRT5XMyJ': 'forearm-supination-articulations', // Forearm - Supination - Articulations
|
||||||
|
'exr_41n2hJFwC7ocdiNm': 'forward-flexion-neck-stretch', // Forward Flexion Neck Stretch
|
||||||
|
'exr_41n2huQw1pHKH9cw': 'front-and-back-neck-stretch', // Front and Back Neck Stretch
|
||||||
|
'exr_41n2htzPyjcc3Mt2': 'front-plank-toe-tap', // Front Plank Toe Tap
|
||||||
|
'exr_41n2hKoQnnSRPZrE': 'front-plank-with-leg-lift', // Front Plank with Leg Lift
|
||||||
|
'exr_41n2hkknYAEEE3tc': 'front-toe-touching', // Front Toe Touching
|
||||||
|
'exr_41n2hGioS8HumEF7': 'hammer-curl-cable', // Hammer Curl (CABLE)
|
||||||
|
'exr_41n2hushK9NGVfyK': 'handstand-push-up', // Handstand Push-Up
|
||||||
|
'exr_41n2hMZCmZBvQApL': 'hanging-straight-leg-raise', // Hanging Straight Leg Raise
|
||||||
|
'exr_41n2hXXpvbykPY3q': 'incline-push-up', // Incline Push-up
|
||||||
|
'exr_41n2hzAMXkkQQ5T2': 'inverted-chin-curl-bent-knee-chairs', // Inverted Chin Curl with Bent Knee between Chairs
|
||||||
|
'exr_41n2hUVNhvcS73Dt': 'jump-split', // Jump Split
|
||||||
|
'exr_41n2hbdZww1thMKz': 'jump-squat', // Jump Squat
|
||||||
|
'exr_41n2hupxPcdnktBC': 'jumping-jack', // Jumping Jack
|
||||||
|
'exr_41n2hGRSg9WCoTYT': 'jumping-pistol-squat', // Jumping Pistol Squat
|
||||||
|
'exr_41n2hpnZ6oASM662': 'kneeling-push-up-to-child-pose', // Kneeling Push up to Child Pose
|
||||||
|
'exr_41n2hSvEPVntpxSG': 'kneeling-rotational-push-up', // Kneeling Rotational Push-up
|
||||||
|
'exr_41n2hyWsNxNYWpk3': 'kneeling-toe-up-hamstring-stretch', // Kneeling Toe Up Hamstring Stretch
|
||||||
|
'exr_41n2hxg75dFGERdp': 'l-pull-up', // L-Pull-up
|
||||||
|
'exr_41n2hU3XPwUFSpkC': 'l-sit-on-floor', // L-Sit on Floor
|
||||||
|
'exr_41n2hS5UrMusVCEE': 'lateral-raise-towel', // Lateral Raise with Towel
|
||||||
|
'exr_41n2hM8vgMA6MREd': 'left-hook-boxing', // Left hook. Boxing
|
||||||
|
'exr_41n2hhiWL8njJDZe': 'lever-seated-calf-raise', // Lever Seated Calf Raise (MACHINE)
|
||||||
|
'exr_41n2hKiaWSZQTqgE': 'lever-stepper', // Lever stepper
|
||||||
|
'exr_41n2hc2VrB8ofxrW': 'lying-double-legs-biceps-curl-towel', // Lying Double Legs Biceps Curl with Towel
|
||||||
|
'exr_41n2hx9wyaRGNyvs': 'lying-double-legs-hammer-curl-towel', // Lying Double Legs Hammer Curl with Towel
|
||||||
|
'exr_41n2hq3Wm6ANkgUz': 'lying-leg-raise-and-hold', // Lying Leg Raise and Hold
|
||||||
|
'exr_41n2hfnnXz9shkBi': 'lying-lower-back-stretch', // Lying Lower Back Stretch
|
||||||
|
'exr_41n2hpJxS5VQKtBL': 'lying-lower-back-stretch-alt', // Lying Lower Back Stretch (variant)
|
||||||
|
'exr_41n2hkB3FeGM3DEL': 'lying-scissor-kick', // Lying Scissor Kick
|
||||||
|
'exr_41n2hhBHuvSdAeCJ': 'neck-circle-stretch', // Neck Circle Stretch
|
||||||
|
'exr_41n2hGbCptD8Nosk': 'neck-side-stretch', // Neck Side Stretch
|
||||||
|
'exr_41n2hvrsUaWWb9Mk': 'neck-side-stretch-alt', // Neck Side Stretch (variant)
|
||||||
|
'exr_41n2hHdjQpnyNdie': 'one-arm-bent-over-row', // One Arm Bent-over Row (DUMBBELL)
|
||||||
|
'exr_41n2hpeHAizgtrEw': 'one-arm-reverse-wrist-curl', // One arm Revers Wrist Curl (DUMBBELL)
|
||||||
|
'exr_41n2hGy6zE7fN6v2': 'one-arm-wrist-curl', // One arm Wrist Curl (DUMBBELL)
|
||||||
|
'exr_41n2hhumxqyAFuTb': 'one-leg-floor-calf-raise', // One Leg Floor Calf Raise
|
||||||
|
'exr_41n2hsVHu7B1MTdr': 'palms-in-incline-bench-press', // Palms In Incline Bench Press (DUMBBELL)
|
||||||
|
'exr_41n2hbYPY4jLKxW3': 'peroneals-stretch', // Peroneals Stretch
|
||||||
|
'exr_41n2hmvGdVRvvnNY': 'pike-push-up', // Pike Push up
|
||||||
|
'exr_41n2hR12rPqdpWP8': 'pike-to-cobra-push-up', // Pike-to-Cobra Push-up
|
||||||
|
'exr_41n2hU4y6EaYXFhr': 'pull-up-alt', // Pull up (variant)
|
||||||
|
'exr_41n2hQEqKxuAfV1D': 'pull-up-bent-knee-chairs', // Pull-up with Bent Knee between Chairs
|
||||||
|
'exr_41n2hmhxk35fbHbC': 'push-up-on-forearms', // Push-up on Forearms
|
||||||
|
'exr_41n2hSMjcavNjk3c': 'quick-feet', // Quick Feet
|
||||||
|
'exr_41n2hGNrmUnF58Yy': 'reverse-lunge', // Reverse Lunge
|
||||||
|
'exr_41n2hdo2vCtq4F3E': 'right-cross-boxing', // Right Cross. Boxing
|
||||||
|
'exr_41n2hwio5ECAfLuS': 'right-hook-boxing', // Right Hook. Boxing
|
||||||
|
'exr_41n2htTnk4CuspZh': 'rotating-neck-stretch', // Rotating Neck Stretch
|
||||||
|
'exr_41n2hjkBReJMbDJk': 'run-on-treadmill', // Run on Treadmill (MACHINE)
|
||||||
|
'exr_41n2hpDWoTxocW8G': 'scissors', // Scissors
|
||||||
|
'exr_41n2hTs4q3ihihZs': 'seated-calf-raise-barbell', // Seated Calf Raise (BARBELL)
|
||||||
|
'exr_41n2hNRM1dGGhGYL': 'seated-flexion-extension-neck', // Seated Flexion And Extension Neck
|
||||||
|
'exr_41n2hcFJpBvAkXCP': 'seated-row-towel', // Seated Row with Towel
|
||||||
|
'exr_41n2hJ5Harig7K7F': 'seated-shoulder-flexor-stretch', // Seated Shoulder Flexor Depresor Retractor Stretch
|
||||||
|
'exr_41n2hw4iksLYXESz': 'seated-shoulder-flexor-stretch-bent-knee', // Seated Shoulder Flexor ... Bent Knee
|
||||||
|
'exr_41n2hPvwqp7Pwvks': 'seated-single-leg-hamstring-stretch', // Seated Single Leg Hamstring Stretch
|
||||||
|
'exr_41n2hu5r8WMaLUkH': 'seated-straight-leg-calf-stretch', // Seated Straight Leg Calf Stretch
|
||||||
|
'exr_41n2hdsGcuzs4WrV': 'self-assisted-inverted-pullover', // Self Assisted Inverted Pullover
|
||||||
|
'exr_41n2hZqkvM55qJve': 'shoulder-stretch-behind-back', // Shoulder Stretch Behind the Back
|
||||||
|
'exr_41n2hnAGfMhp95LQ': 'shoulder-tap-push-up', // Shoulder Tap Push-up
|
||||||
|
'exr_41n2haAabPyN5t8y': 'side-lunge', // Side Lunge
|
||||||
|
'exr_41n2hRaLxY7YfNbg': 'side-lunge-stretch', // Side Lunge Stretch
|
||||||
|
'exr_41n2hrN2RCZBZU9h': 'side-neck-stretch', // Side Neck Stretch
|
||||||
|
'exr_41n2hsZWJA1ujZUd': 'side-push-neck-stretch', // Side Push Neck Stretch
|
||||||
|
'exr_41n2hw1QspZ6uXoW': 'side-toe-touching', // Side Toe Touching
|
||||||
|
'exr_41n2hMydkzFvswVX': 'side-two-front-toe-touching', // Side Two Front Toe Touching
|
||||||
|
'exr_41n2hPxDaq9kFjiL': 'side-wrist-pull-stretch', // Side Wrist Pull Stretch
|
||||||
|
'exr_41n2hLpyk6MsV85U': 'single-leg-deadlift-arm-extended', // Single Leg Bodyweight Deadlift with Arm and Leg Extended
|
||||||
|
'exr_41n2hd78zujKUEWK': 'single-leg-squat', // Single Leg Squat
|
||||||
|
'exr_41n2hsSnmS946i2k': 'single-leg-squat-with-support', // Single Leg Squat with Support
|
||||||
|
'exr_41n2hw8nSYiaCXW1': 'sissy-squat', // Sissy Squat
|
||||||
|
'exr_41n2hvjrFJ2KjzGm': 'sit', // Sit
|
||||||
|
'exr_41n2hnFD2bT6sruf': 'sliding-floor-bridge-curl-towel', // Sliding Floor Bridge Curl on Towel
|
||||||
|
'exr_41n2hadPLLFRGvFk': 'sliding-floor-pulldown-towel', // Sliding Floor Pulldown on Towel
|
||||||
|
'exr_41n2hSxsNAV8tGS6': 'split-squat', // Split Squat
|
||||||
|
'exr_41n2hHRszDHarrxK': 'split-squats', // Split Squats (variant)
|
||||||
|
'exr_41n2hmGR8WuVfe1U': 'squat', // Squat (bodyweight)
|
||||||
|
'exr_41n2hRicz5MdZEns': 'squat-thrust', // Squat Thrust
|
||||||
|
'exr_41n2homrPqqs8coG': 'stair-up', // Stair Up
|
||||||
|
'exr_41n2hSq88Ni3KCny': 'standing-alternate-arms-circling', // Standing Alternate Arms Circling
|
||||||
|
'exr_41n2hUBVSgXaKhau': 'standing-arms-circling', // Standing Arms Circling
|
||||||
|
'exr_41n2hynD9srC1kY7': 'standing-arms-flinging', // Standing Arms Flinging
|
||||||
|
'exr_41n2hzfRXQDaLYJh': 'standing-calf-raise', // Standing Calf Raise (bodyweight)
|
||||||
|
'exr_41n2hdWu3oaCGdWT': 'standing-calf-raise-dumbbell', // Standing Calf Raise (DUMBBELL)
|
||||||
|
'exr_41n2hvzxocyjoGgL': 'standing-leg-calf-raise-barbell', // Standing Leg Calf Raise (BARBELL)
|
||||||
|
'exr_41n2huXeEFSaqo4G': 'standing-one-arm-circling', // Standing One Arm Circling
|
||||||
|
'exr_41n2hXYRxFHnQAD4': 'stationary-bike-run', // Stationary Bike Run (MACHINE)
|
||||||
|
'exr_41n2hXvPyEyMBgNR': 'step-up-on-chair', // Step-up on Chair
|
||||||
|
'exr_41n2hYAP9oGEZk2P': 'sumo-squat', // Sumo Squat
|
||||||
|
'exr_41n2hWxnJoGwbJpa': 'superman-row-towel', // Superman Row with Towel
|
||||||
|
'exr_41n2hoifHqpb7WK9': 'supination-bar-suspension-stretch', // Supination Bar Suspension Stretch
|
||||||
|
'exr_41n2hdkBpqwoDmVq': 'suspended-row', // Suspended Row
|
||||||
|
'exr_41n2hLUqpev5gSzJ': 'thoracic-bridge', // Thoracic Bridge
|
||||||
|
'exr_41n2hndkoGHD1ogh': 'tricep-dip-alt', // Triceps Dip (variant)
|
||||||
|
'exr_41n2hHH9bNfi98YU': 'triceps-dips-floor', // Triceps Dips Floor
|
||||||
|
'exr_41n2hLA8xydD4dzE': 'triceps-press', // Triceps Press
|
||||||
|
'exr_41n2hYWXejezzLjv': 'two-front-toe-touching', // Two Front Toe Touching
|
||||||
|
'exr_41n2hqjVS3nwBoyr': 'two-legs-hammer-curl-towel', // Two Legs Hammer Curl with Towel
|
||||||
|
'exr_41n2hx6oyEujP1B6': 'two-legs-reverse-biceps-curl-towel', // Two Legs Reverse Biceps Curl with Towel
|
||||||
|
'exr_41n2huc12BsuDNYQ': 'v-up', // V-up
|
||||||
|
'exr_41n2hLZZAH2F2UkS': 'walk-elliptical-cross-trainer', // Walk Elliptical Cross Trainer (MACHINE)
|
||||||
|
'exr_41n2hmFcGGUCS289': 'walking-high-knees-lunge', // Walking High Knees Lunge
|
||||||
|
'exr_41n2hQHmRSoUkk9F': 'walking-lunge', // Walking Lunge
|
||||||
|
'exr_41n2hkCHzg1AXdkV': 'walking-on-incline-treadmill', // Walking on Incline Treadmill (MACHINE)
|
||||||
|
'exr_41n2hoyHUrhBiEWg': 'walking-on-treadmill', // Walking on Treadmill (MACHINE)
|
||||||
|
'exr_41n2hftBVLiXgtRQ': 'wide-grip-pull-up', // Wide Grip Pull-Up
|
||||||
|
'exr_41n2hWgAAtQeA3Lh': 'wide-hand-push-up', // Wide Hand Push up
|
||||||
|
'exr_41n2hozyXuCmDTdZ': 'wrist-circles', // Wrist Circles
|
||||||
|
'exr_41n2hSF5U97sFAr8': 'wrist-extension-articulations', // Wrist - Extension - Articulations
|
||||||
|
'exr_41n2hQcqPQ37Dmxj': 'wrist-flexion-articulations', // Wrist - Flexion - Articulations
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Reverse lookup: internal slug → ExerciseDB ID */
|
||||||
|
export const slugToExerciseDbId: Record<string, string> = Object.fromEntries(
|
||||||
|
Object.entries(exerciseDbMap).map(([edbId, slug]) => [slug, edbId])
|
||||||
|
);
|
||||||
14190
src/lib/data/exercisedb-raw.json
Normal file
BIN
static/fitness/exercises/exr_41n2hG9pRT55cGVk/1080p.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
static/fitness/exercises/exr_41n2hG9pRT55cGVk/360p.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/fitness/exercises/exr_41n2hG9pRT55cGVk/480p.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/fitness/exercises/exr_41n2hG9pRT55cGVk/720p.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
static/fitness/exercises/exr_41n2hG9pRT55cGVk/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hGD4omjWVnbS/1080p.jpg
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
static/fitness/exercises/exr_41n2hGD4omjWVnbS/360p.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/fitness/exercises/exr_41n2hGD4omjWVnbS/480p.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/fitness/exercises/exr_41n2hGD4omjWVnbS/720p.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
static/fitness/exercises/exr_41n2hGD4omjWVnbS/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hGNrmUnF58Yy/1080p.jpg
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
static/fitness/exercises/exr_41n2hGNrmUnF58Yy/360p.jpg
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
static/fitness/exercises/exr_41n2hGNrmUnF58Yy/480p.jpg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
static/fitness/exercises/exr_41n2hGNrmUnF58Yy/720p.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
static/fitness/exercises/exr_41n2hGNrmUnF58Yy/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hGRSg9WCoTYT/1080p.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
static/fitness/exercises/exr_41n2hGRSg9WCoTYT/360p.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
static/fitness/exercises/exr_41n2hGRSg9WCoTYT/480p.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
static/fitness/exercises/exr_41n2hGRSg9WCoTYT/720p.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
static/fitness/exercises/exr_41n2hGRSg9WCoTYT/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hGUso7JFmuYR/1080p.jpg
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
static/fitness/exercises/exr_41n2hGUso7JFmuYR/360p.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/fitness/exercises/exr_41n2hGUso7JFmuYR/480p.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/fitness/exercises/exr_41n2hGUso7JFmuYR/720p.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
static/fitness/exercises/exr_41n2hGUso7JFmuYR/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hGbCptD8Nosk/1080p.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
static/fitness/exercises/exr_41n2hGbCptD8Nosk/360p.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
static/fitness/exercises/exr_41n2hGbCptD8Nosk/480p.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
static/fitness/exercises/exr_41n2hGbCptD8Nosk/720p.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
static/fitness/exercises/exr_41n2hGbCptD8Nosk/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hGioS8HumEF7/1080p.jpg
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
static/fitness/exercises/exr_41n2hGioS8HumEF7/360p.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
static/fitness/exercises/exr_41n2hGioS8HumEF7/480p.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
static/fitness/exercises/exr_41n2hGioS8HumEF7/720p.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
static/fitness/exercises/exr_41n2hGioS8HumEF7/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hGy6zE7fN6v2/1080p.jpg
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
static/fitness/exercises/exr_41n2hGy6zE7fN6v2/360p.jpg
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
static/fitness/exercises/exr_41n2hGy6zE7fN6v2/480p.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
static/fitness/exercises/exr_41n2hGy6zE7fN6v2/720p.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
static/fitness/exercises/exr_41n2hGy6zE7fN6v2/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hH6VGNz6cNtv/1080p.jpg
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
static/fitness/exercises/exr_41n2hH6VGNz6cNtv/360p.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/fitness/exercises/exr_41n2hH6VGNz6cNtv/480p.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/fitness/exercises/exr_41n2hH6VGNz6cNtv/720p.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
static/fitness/exercises/exr_41n2hH6VGNz6cNtv/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hHCXQpZYhxhc/1080p.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
static/fitness/exercises/exr_41n2hHCXQpZYhxhc/360p.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/fitness/exercises/exr_41n2hHCXQpZYhxhc/480p.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
static/fitness/exercises/exr_41n2hHCXQpZYhxhc/720p.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
static/fitness/exercises/exr_41n2hHCXQpZYhxhc/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hHH9bNfi98YU/1080p.jpg
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
static/fitness/exercises/exr_41n2hHH9bNfi98YU/360p.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
static/fitness/exercises/exr_41n2hHH9bNfi98YU/480p.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
static/fitness/exercises/exr_41n2hHH9bNfi98YU/720p.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
static/fitness/exercises/exr_41n2hHH9bNfi98YU/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hHLE8aJXaxKR/1080p.jpg
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
static/fitness/exercises/exr_41n2hHLE8aJXaxKR/360p.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/fitness/exercises/exr_41n2hHLE8aJXaxKR/480p.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/fitness/exercises/exr_41n2hHLE8aJXaxKR/720p.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
static/fitness/exercises/exr_41n2hHLE8aJXaxKR/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hHRszDHarrxK/1080p.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
static/fitness/exercises/exr_41n2hHRszDHarrxK/360p.jpg
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
static/fitness/exercises/exr_41n2hHRszDHarrxK/480p.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
static/fitness/exercises/exr_41n2hHRszDHarrxK/720p.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
static/fitness/exercises/exr_41n2hHRszDHarrxK/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hHdjQpnyNdie/1080p.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
static/fitness/exercises/exr_41n2hHdjQpnyNdie/360p.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
static/fitness/exercises/exr_41n2hHdjQpnyNdie/480p.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
static/fitness/exercises/exr_41n2hHdjQpnyNdie/720p.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
static/fitness/exercises/exr_41n2hHdjQpnyNdie/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hJ5Harig7K7F/1080p.jpg
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
static/fitness/exercises/exr_41n2hJ5Harig7K7F/360p.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
static/fitness/exercises/exr_41n2hJ5Harig7K7F/480p.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
static/fitness/exercises/exr_41n2hJ5Harig7K7F/720p.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
static/fitness/exercises/exr_41n2hJ5Harig7K7F/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hJFwC7ocdiNm/1080p.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
static/fitness/exercises/exr_41n2hJFwC7ocdiNm/360p.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
static/fitness/exercises/exr_41n2hJFwC7ocdiNm/480p.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
static/fitness/exercises/exr_41n2hJFwC7ocdiNm/720p.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
static/fitness/exercises/exr_41n2hJFwC7ocdiNm/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hKZmyYXB2UL4/1080p.jpg
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
static/fitness/exercises/exr_41n2hKZmyYXB2UL4/360p.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/fitness/exercises/exr_41n2hKZmyYXB2UL4/480p.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/fitness/exercises/exr_41n2hKZmyYXB2UL4/720p.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
static/fitness/exercises/exr_41n2hKZmyYXB2UL4/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hKiaWSZQTqgE/1080p.jpg
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
static/fitness/exercises/exr_41n2hKiaWSZQTqgE/360p.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
static/fitness/exercises/exr_41n2hKiaWSZQTqgE/480p.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
static/fitness/exercises/exr_41n2hKiaWSZQTqgE/720p.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
static/fitness/exercises/exr_41n2hKiaWSZQTqgE/video.mp4
Normal file
BIN
static/fitness/exercises/exr_41n2hKoQnnSRPZrE/1080p.jpg
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
static/fitness/exercises/exr_41n2hKoQnnSRPZrE/360p.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
static/fitness/exercises/exr_41n2hKoQnnSRPZrE/480p.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
static/fitness/exercises/exr_41n2hKoQnnSRPZrE/720p.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |