ive-connect 0.1.2 → 0.2.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.
package/README.md CHANGED
@@ -1,109 +1,109 @@
1
- # ive-connect
2
-
3
- A universal haptic device control library that provides a consistent interface for managing various haptic devices (Handy, Buttplug, etc.).
4
-
5
- ## Features
6
-
7
- - Unified device control interface
8
- - Support for multiple device types
9
- - Event-based state management
10
- - TypeScript support
11
-
12
- ## Installation
13
-
14
- ```bash
15
- npm install ive-connect
16
- ```
17
-
18
- ## Quick Start
19
-
20
- ```typescript
21
- import { DeviceManager, HandyDevice } from "ive-connect";
22
-
23
- // Create a device manager
24
- const manager = new DeviceManager();
25
-
26
- // Create and register a Handy device
27
- const handyDevice = new HandyDevice({
28
- connectionKey: "your-connection-key",
29
- });
30
- manager.registerDevice(handyDevice);
31
-
32
- // Connect to the device
33
- await handyDevice.connect();
34
-
35
- // Load a script
36
- await handyDevice.loadScript({
37
- type: "funscript",
38
- url: "https://example.com/script.funscript",
39
- });
40
-
41
- // Start playback
42
- await handyDevice.play(0, 1.0, false);
43
-
44
- // Later, stop playback
45
- await handyDevice.stop();
46
-
47
- // Disconnect when done
48
- await handyDevice.disconnect();
49
- ```
50
-
51
- ## Supported Devices
52
-
53
- ### Handy
54
-
55
- ```typescript
56
- import { HandyDevice } from "ive-connect";
57
-
58
- const handy = new HandyDevice({
59
- connectionKey: "your-connection-key",
60
- // Optional custom configuration
61
- baseV3Url: "https://www.handyfeeling.com/api/v3",
62
- baseV2Url: "https://www.handyfeeling.com/api/v2",
63
- applicationId: "YourAppName",
64
- });
65
-
66
- // Connect to the device
67
- await handy.connect();
68
-
69
- // Update configuration
70
- await handy.updateConfig({
71
- offset: -200, // Timing offset in milliseconds
72
- stroke: {
73
- min: 0.1, // Min stroke position (0.0 to 1.0)
74
- max: 0.9, // Max stroke position (0.0 to 1.0)
75
- },
76
- });
77
-
78
- // Listen for events
79
- handy.on("connected", (deviceInfo) => {
80
- console.log("Connected to Handy:", deviceInfo);
81
- });
82
-
83
- handy.on("playbackStateChanged", (state) => {
84
- console.log("Playback state changed:", state.isPlaying);
85
- });
86
- ```
87
-
88
- ### Using the Device Manager
89
-
90
- ```typescript
91
- import { DeviceManager, HandyDevice } from "ive-connect";
92
-
93
- // Create a device manager
94
- const manager = new DeviceManager();
95
-
96
- // Register devices
97
- const handy = new HandyDevice({
98
- connectionKey: "your-connection-key",
99
- });
100
- manager.registerDevice(handy);
101
- ```
102
-
103
- ## License
104
-
105
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
106
-
107
- ## Acknowledgments
108
-
109
- This project uses [buttplug.io](https://buttplug.io) (BSD 3-Clause License) for device communication.
1
+ # ive-connect
2
+
3
+ A universal haptic device control library that provides a consistent interface for managing various haptic devices (Handy, Buttplug, etc.).
4
+
5
+ ## Features
6
+
7
+ - Unified device control interface
8
+ - Support for multiple device types
9
+ - Event-based state management
10
+ - TypeScript support
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install ive-connect
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```typescript
21
+ import { DeviceManager, HandyDevice } from "ive-connect";
22
+
23
+ // Create a device manager
24
+ const manager = new DeviceManager();
25
+
26
+ // Create and register a Handy device
27
+ const handyDevice = new HandyDevice({
28
+ connectionKey: "your-connection-key",
29
+ });
30
+ manager.registerDevice(handyDevice);
31
+
32
+ // Connect to the device
33
+ await handyDevice.connect();
34
+
35
+ // Load a script
36
+ await handyDevice.loadScript({
37
+ type: "funscript",
38
+ url: "https://example.com/script.funscript",
39
+ });
40
+
41
+ // Start playback
42
+ await handyDevice.play(0, 1.0, false);
43
+
44
+ // Later, stop playback
45
+ await handyDevice.stop();
46
+
47
+ // Disconnect when done
48
+ await handyDevice.disconnect();
49
+ ```
50
+
51
+ ## Supported Devices
52
+
53
+ ### Handy
54
+
55
+ ```typescript
56
+ import { HandyDevice } from "ive-connect";
57
+
58
+ const handy = new HandyDevice({
59
+ connectionKey: "your-connection-key",
60
+ // Optional custom configuration
61
+ baseV3Url: "https://www.handyfeeling.com/api/v3",
62
+ baseV2Url: "https://www.handyfeeling.com/api/v2",
63
+ applicationId: "YourAppName",
64
+ });
65
+
66
+ // Connect to the device
67
+ await handy.connect();
68
+
69
+ // Update configuration
70
+ await handy.updateConfig({
71
+ offset: -200, // Timing offset in milliseconds
72
+ stroke: {
73
+ min: 0.1, // Min stroke position (0.0 to 1.0)
74
+ max: 0.9, // Max stroke position (0.0 to 1.0)
75
+ },
76
+ });
77
+
78
+ // Listen for events
79
+ handy.on("connected", (deviceInfo) => {
80
+ console.log("Connected to Handy:", deviceInfo);
81
+ });
82
+
83
+ handy.on("playbackStateChanged", (state) => {
84
+ console.log("Playback state changed:", state.isPlaying);
85
+ });
86
+ ```
87
+
88
+ ### Using the Device Manager
89
+
90
+ ```typescript
91
+ import { DeviceManager, HandyDevice } from "ive-connect";
92
+
93
+ // Create a device manager
94
+ const manager = new DeviceManager();
95
+
96
+ // Register devices
97
+ const handy = new HandyDevice({
98
+ connectionKey: "your-connection-key",
99
+ });
100
+ manager.registerDevice(handy);
101
+ ```
102
+
103
+ ## License
104
+
105
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
106
+
107
+ ## Acknowledgments
108
+
109
+ This project uses [buttplug.io](https://buttplug.io) (BSD 3-Clause License) for device communication.
@@ -43,7 +43,7 @@ const buttplug_1 = require("buttplug");
43
43
  const buttplug_2 = require("buttplug");
44
44
  const events_1 = require("../../core/events");
45
45
  const types_1 = require("./types");
46
- const DEBUG_WEBSOCKET = true;
46
+ const DEBUG_WEBSOCKET = false;
47
47
  class ButtplugApi extends events_1.EventEmitter {
48
48
  constructor(clientName = "IVE-Connect") {
49
49
  super();
@@ -411,11 +411,11 @@ class ButtplugDevice extends events_1.EventEmitter {
411
411
  const prevAction = actionIndex > 0
412
412
  ? this._currentScriptActions[actionIndex - 1]
413
413
  : { pos: 0 };
414
- // Calculate duration for linear movement based on time to next action
414
+ // Calculate duration for linear movement based on time with previous action
415
415
  let durationMs = 500; // Default duration if we can't determine
416
416
  if (actionIndex < this._currentScriptActions.length - 1) {
417
- const nextAction = this._currentScriptActions[actionIndex + 1];
418
- durationMs = nextAction.at - action.at;
417
+ const prevAction = this._currentScriptActions[actionIndex - 1];
418
+ durationMs = action.at - prevAction.at;
419
419
  // Enforce a minimum duration to prevent erratic movement
420
420
  durationMs = Math.max(100, durationMs);
421
421
  }
@@ -458,7 +458,12 @@ class ButtplugDevice extends events_1.EventEmitter {
458
458
  high = mid - 1;
459
459
  }
460
460
  }
461
- return bestIndex;
461
+ // When returning bestIndex, we're always getting
462
+ // the last action that has a timestamp <= timeMs
463
+ // Let's return the next action instead
464
+ return bestIndex < this._currentScriptActions.length - 1
465
+ ? bestIndex + 1
466
+ : bestIndex;
462
467
  }
463
468
  /**
464
469
  * Set up event handlers for the Buttplug API
@@ -166,7 +166,7 @@ class HandyApi {
166
166
  const response = await this.request("/hssp/play", {
167
167
  method: "PUT",
168
168
  body: JSON.stringify({
169
- start_time: Math.round(videoTime * 1000),
169
+ start_time: Math.round(videoTime),
170
170
  server_time: this.estimateServerTime(),
171
171
  playback_rate: playbackRate,
172
172
  loop,
@@ -203,7 +203,7 @@ class HandyApi {
203
203
  const response = await this.request("/hssp/synctime", {
204
204
  method: "PUT",
205
205
  body: JSON.stringify({
206
- current_time: Math.round(videoTime * 1000),
206
+ current_time: Math.round(videoTime),
207
207
  server_time: this.estimateServerTime(),
208
208
  filter,
209
209
  }),
@@ -205,8 +205,32 @@ class HandyDevice extends events_1.EventEmitter {
205
205
  let scriptUrl;
206
206
  // Handle script data based on type
207
207
  if (scriptData.url) {
208
- // If URL is provided, use it directly
209
- scriptUrl = scriptData.url;
208
+ // If URL ends with .funscript and it's a direct URL, fetch and upload it
209
+ if (scriptData.url.toLowerCase().endsWith(".funscript")) {
210
+ try {
211
+ const response = await fetch(scriptData.url);
212
+ if (!response.ok) {
213
+ throw new Error(`Failed to fetch funscript: ${response.status}`);
214
+ }
215
+ const funscriptContent = await response.json();
216
+ const blob = new Blob([JSON.stringify(funscriptContent)], {
217
+ type: "application/json",
218
+ });
219
+ const uploadedUrl = await this._api.uploadScript(blob);
220
+ if (!uploadedUrl) {
221
+ throw new Error("Failed to upload funscript");
222
+ }
223
+ scriptUrl = uploadedUrl;
224
+ }
225
+ catch (error) {
226
+ console.error("Error processing funscript URL:", error);
227
+ // Fall back to using the original URL
228
+ scriptUrl = scriptData.url;
229
+ }
230
+ }
231
+ else {
232
+ scriptUrl = scriptData.url;
233
+ }
210
234
  }
211
235
  else if (scriptData.content) {
212
236
  // If content is provided, upload it
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ive-connect",
3
- "version": "0.1.2",
3
+ "version": "0.2.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",
@@ -10,7 +10,8 @@
10
10
  "prebuild": "npm run clean",
11
11
  "prepare": "npm run build",
12
12
  "test": "jest",
13
- "lint": "eslint src --ext .ts"
13
+ "lint": "eslint src --ext .ts",
14
+ "release": "release-it"
14
15
  },
15
16
  "keywords": [
16
17
  "haptic",
@@ -37,6 +38,7 @@
37
38
  "@typescript-eslint/parser": "^8.31.0",
38
39
  "eslint": "^9.25.1",
39
40
  "jest": "^29.7.0",
41
+ "release-it": "^19.0.2",
40
42
  "rimraf": "^6.0.1"
41
43
  },
42
44
  "engines": {