ive-connect 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ const buttplug_api_1 = require("./buttplug-api");
12
12
  const types_1 = require("./types");
13
13
  const buttplug_server_1 = require("./buttplug-server");
14
14
  const command_helpers_1 = require("./command-helpers");
15
+ const parseCSVToFunscript_1 = require("../../utils/parseCSVToFunscript");
15
16
  /**
16
17
  * Default Buttplug configuration
17
18
  */
@@ -193,7 +194,7 @@ class ButtplugDevice extends events_1.EventEmitter {
193
194
  * Load a script for playback
194
195
  */
195
196
  async loadScript(scriptData) {
196
- var _a;
197
+ var _a, _b, _c;
197
198
  if (!this.isConnected) {
198
199
  this.emit("error", "Cannot load script: Not connected to a server");
199
200
  return false;
@@ -213,8 +214,34 @@ class ButtplugDevice extends events_1.EventEmitter {
213
214
  if (!response.ok) {
214
215
  throw new Error(`Failed to fetch script: ${response.status} ${response.statusText}`);
215
216
  }
216
- scriptContent = await response.json();
217
- console.log(`[BUTTPLUG-SCRIPT] Script loaded successfully, actions:`, (_a = scriptContent.actions) === null || _a === void 0 ? void 0 : _a.length);
217
+ // Determine if it's a CSV or JSON (funscript) based on file extension
218
+ const fileExtension = scriptData.url.toLowerCase().split(".").pop();
219
+ if (fileExtension === "csv") {
220
+ // Handle CSV file
221
+ const csvText = await response.text();
222
+ scriptContent = (0, parseCSVToFunscript_1.parseCSVToFunscript)(csvText);
223
+ console.log(`[BUTTPLUG-SCRIPT] CSV loaded and converted to funscript format, actions:`, (_a = scriptContent.actions) === null || _a === void 0 ? void 0 : _a.length);
224
+ }
225
+ else {
226
+ // Handle JSON file (funscript)
227
+ try {
228
+ scriptContent = await response.json();
229
+ console.log(`[BUTTPLUG-SCRIPT] Script loaded successfully, actions:`, (_b = scriptContent.actions) === null || _b === void 0 ? void 0 : _b.length);
230
+ }
231
+ catch (parseError) {
232
+ // If JSON parsing fails, try as CSV
233
+ const text = await response.text();
234
+ try {
235
+ // First try to parse as JSON again with some cleanup
236
+ scriptContent = JSON.parse(text.trim());
237
+ }
238
+ catch (_d) {
239
+ // If that fails, try CSV parsing
240
+ scriptContent = (0, parseCSVToFunscript_1.parseCSVToFunscript)(text);
241
+ }
242
+ console.log(`[BUTTPLUG-SCRIPT] File loaded and parsed as CSV, actions:`, (_c = scriptContent.actions) === null || _c === void 0 ? void 0 : _c.length);
243
+ }
244
+ }
218
245
  }
219
246
  catch (error) {
220
247
  this.emit("error", `Failed to fetch script: ${error instanceof Error ? error.message : String(error)}`);
@@ -382,6 +409,7 @@ class ButtplugDevice extends events_1.EventEmitter {
382
409
  * Process script actions based on current time
383
410
  */
384
411
  _processActions(executor) {
412
+ var _a;
385
413
  if (!this._isPlaying || !this._currentScriptActions.length) {
386
414
  return;
387
415
  }
@@ -392,7 +420,7 @@ class ButtplugDevice extends events_1.EventEmitter {
392
420
  const actionIndex = this._findActionIndexForTime(elapsedMs);
393
421
  // If we reached the end of the script
394
422
  if (actionIndex === this._currentScriptActions.length - 1 &&
395
- elapsedMs > this._currentScriptActions[actionIndex].at + 1000) {
423
+ elapsedMs > ((_a = this._currentScriptActions[actionIndex]) === null || _a === void 0 ? void 0 : _a.at) + 1000) {
396
424
  if (this._loopPlayback) {
397
425
  // Reset for loop playback
398
426
  this._playbackStartTime = Date.now();
@@ -415,7 +443,7 @@ class ButtplugDevice extends events_1.EventEmitter {
415
443
  let durationMs = 500; // Default duration if we can't determine
416
444
  if (actionIndex < this._currentScriptActions.length - 1) {
417
445
  const prevAction = this._currentScriptActions[actionIndex - 1];
418
- durationMs = action.at - prevAction.at;
446
+ durationMs = (action === null || action === void 0 ? void 0 : action.at) - (prevAction === null || prevAction === void 0 ? void 0 : prevAction.at);
419
447
  // Enforce a minimum duration to prevent erratic movement
420
448
  durationMs = Math.max(100, durationMs);
421
449
  }
@@ -0,0 +1,7 @@
1
+ export declare const parseCSVToFunscript: (csvText: string) => {
2
+ actions: {
3
+ at: number;
4
+ pos: number;
5
+ }[];
6
+ name: string;
7
+ };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseCSVToFunscript = void 0;
4
+ const parseCSVToFunscript = (csvText) => {
5
+ try {
6
+ // Split the CSV by lines and filter out empty lines
7
+ const lines = csvText
8
+ .split(/\r?\n/)
9
+ .filter((line) => line.trim().length > 0);
10
+ const actions = [];
11
+ // Check if there's a header line (contains non-numeric characters in first column)
12
+ let startIndex = 0;
13
+ if (isNaN(parseFloat(lines[0].split(",")[0]))) {
14
+ startIndex = 1;
15
+ }
16
+ // Parse each line
17
+ for (let i = startIndex; i < lines.length; i++) {
18
+ const line = lines[i].trim();
19
+ if (!line)
20
+ continue;
21
+ const columns = line.split(",");
22
+ actions.push({
23
+ at: Math.round(parseFloat(columns[0].trim())),
24
+ pos: Math.min(100, Math.max(0, Math.round(parseFloat(columns[1].trim())))),
25
+ });
26
+ }
27
+ return {
28
+ actions: actions,
29
+ name: "Converted from CSV",
30
+ };
31
+ }
32
+ catch (error) {
33
+ console.error("Error parsing CSV:", error);
34
+ throw new Error("Failed to parse CSV file");
35
+ }
36
+ };
37
+ exports.parseCSVToFunscript = parseCSVToFunscript;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ive-connect",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A universal haptic device control library for interactive experiences",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",