UNPKG

ive-connect

Version:

A universal haptic device control library for interactive experiences

173 lines (172 loc) 5.7 kB
"use strict"; /** * Script Loader * * Centralized script fetching and parsing. * Handles fetching from URLs, parsing CSV/JSON, and applying transformations. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.parseCSVToFunscript = parseCSVToFunscript; exports.invertFunscript = invertFunscript; exports.isValidFunscript = isValidFunscript; exports.loadScript = loadScript; /** * Parse CSV content to Funscript format */ function parseCSVToFunscript(csvText) { const lines = csvText.split(/\r?\n/).filter((line) => line.trim().length > 0); const actions = []; // Check if there's a header line (contains non-numeric characters in first column) let startIndex = 0; if (lines.length > 0 && isNaN(parseFloat(lines[0].split(",")[0]))) { startIndex = 1; } // Parse each line for (let i = startIndex; i < lines.length; i++) { const line = lines[i].trim(); if (!line) continue; const columns = line.split(","); if (columns.length >= 2) { const at = parseFloat(columns[0].trim()); const pos = parseFloat(columns[1].trim()); if (!isNaN(at) && !isNaN(pos)) { actions.push({ at: Math.round(at), pos: Math.min(100, Math.max(0, Math.round(pos))), }); } } } return { actions, metadata: { convertedFrom: "csv" }, }; } /** * Apply inversion to funscript actions */ function invertFunscript(funscript) { return { ...funscript, actions: funscript.actions.map((action) => ({ ...action, pos: 100 - action.pos, })), inverted: !funscript.inverted, }; } /** * Validate funscript structure */ function isValidFunscript(content) { if (!content || typeof content !== "object") { return false; } const obj = content; if (!Array.isArray(obj.actions)) { return false; } // Check that actions have the required properties return obj.actions.every((action) => action && typeof action === "object" && typeof action.at === "number" && typeof action.pos === "number"); } /** * Load and parse a script from ScriptData * * @param scriptData - The script data (URL or content) * @param options - Script options (e.g., inversion) * @returns Parsed funscript or error */ async function loadScript(scriptData, options) { try { let funscript; if (scriptData.content) { // Content already provided if (!isValidFunscript(scriptData.content)) { return { success: false, funscript: null, error: "Invalid funscript format: content is not a valid funscript", }; } funscript = scriptData.content; } else if (scriptData.url) { // Fetch from URL const response = await fetch(scriptData.url); if (!response.ok) { return { success: false, funscript: null, error: `Failed to fetch script: ${response.status} ${response.statusText}`, }; } const fileExtension = scriptData.url.toLowerCase().split(".").pop(); if (fileExtension === "csv") { const csvText = await response.text(); funscript = parseCSVToFunscript(csvText); } else { // Assume JSON/funscript const text = await response.text(); try { const parsed = JSON.parse(text); if (!isValidFunscript(parsed)) { return { success: false, funscript: null, error: "Invalid funscript format: missing or invalid actions array", }; } funscript = parsed; } catch (_a) { // Try parsing as CSV if JSON fails funscript = parseCSVToFunscript(text); if (funscript.actions.length === 0) { return { success: false, funscript: null, error: "Failed to parse script: not valid JSON or CSV", }; } } } } else { return { success: false, funscript: null, error: "Invalid script data: either URL or content must be provided", }; } // Validate we have actions if (!funscript.actions || funscript.actions.length === 0) { return { success: false, funscript: null, error: "Invalid funscript: no actions found", }; } // Apply inversion if requested if (options === null || options === void 0 ? void 0 : options.invertScript) { funscript = invertFunscript(funscript); } // Sort actions by timestamp funscript.actions.sort((a, b) => a.at - b.at); return { success: true, funscript, }; } catch (error) { return { success: false, funscript: null, error: `Script loading error: ${error instanceof Error ? error.message : String(error)}`, }; } }