{"_id":"videokitten","_rev":"2-6daccf9ea783f4350594588f71241c4e","name":"videokitten","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"videokitten","version":"1.0.0","keywords":["video","recording","adb","xcrun","scrcpy","ios","android","simulator","emulator","screen-recording"],"author":{"name":"Yaroslav Serhieiev","email":"yaroslavs@wix.com"},"license":"MIT","_id":"videokitten@1.0.0","maintainers":[{"name":"yaroslavs","email":"yaroslavs@wix.com"}],"homepage":"https://github.com/wix-incubator/videokitten#readme","bugs":{"url":"https://github.com/wix-incubator/videokitten/issues"},"dist":{"shasum":"9a5e227a46d74df37dc5feae5b33d9cd9904ec73","tarball":"https://registry.npmjs.org/videokitten/-/videokitten-1.0.0.tgz","fileCount":41,"integrity":"sha512-5UmLLXyhK2HEfjSRA3eBtiLkmAS/BWxV41sL7ICh/FDN0kzIsAIefBsbUkEfvg+Ug0Df3Izl/LVprRfFaklvrA==","signatures":[{"sig":"MEQCICxVh9q7/uYLXonllkrCD+192mMTercdGHMH/sAc2+W+AiBbqjdtX7+0YZ5iEx2S064G6k1nkUD8BIaYf2NBmN23Fg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":332075},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=16.14.0"},"exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"bc2c0e577ea6e8f34f6e4fbaedcbe46b67c96549","scripts":{"lint":"eslint . --fix","test":"node --require ts-node/register --test src/*.test.ts","build":"npm run build:types && npm run build:js","lint:ci":"eslint .","prepare":"husky || true","build:js":"node scripts/esbuild.mjs","build:types":"tsc --emitDeclarationOnly","lint:staged":"lint-staged"},"_npmUser":{"name":"yaroslavs","email":"yaroslavs@wix.com"},"repository":{"url":"git+https://github.com/wix-incubator/videokitten.git","type":"git"},"_npmVersion":"10.9.2","description":"A cross-platform Node.js library for recording videos from iOS simulators and Android devices/emulators","directories":{},"_nodeVersion":"22.18.0","browserslist":["node 16"],"dependencies":{"execa":"^5.1.1"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.0.0","husky":"^9.1.7","eslint":"~9.14.0","esbuild":"^0.24.0","ts-node":"^10.9.2","prettier":"^3.4.2","@eslint/js":"^9.13.0","typescript":"^5.6.3","@types/chai":"^4.0.1","@types/node":"^20.17.9","lint-staged":"^15.2.10","@commitlint/cli":"^17.4.2","semantic-release":"^24.2.0","@types/eslint__js":"^8.42.3","typescript-eslint":"^8.10.0","eslint-plugin-jsdoc":"^50.2.2","eslint-plugin-import":"^2.31.0","eslint-plugin-unicorn":"^55.0.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.2.1","@typescript-eslint/parser":"^8.4.0","cz-conventional-changelog":"^3.3.0","eslint-plugin-prefer-arrow":"^1.2.3","@commitlint/config-conventional":"^17.4.2","@typescript-eslint/eslint-plugin":"^8.4.0"},"_npmOperationalInternal":{"tmp":"tmp/videokitten_1.0.0_1755023590518_0.5386454207873514","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"videokitten","version":"1.0.1","description":"A cross-platform Node.js library for recording videos from iOS simulators and Android devices/emulators","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js"},"./package.json":"./package.json"},"engines":{"node":">=16.14.0"},"scripts":{"prepare":"husky || true","build:types":"tsc --emitDeclarationOnly","build:js":"node scripts/esbuild.mjs","build":"npm run build:types && npm run build:js","lint":"eslint . --fix","lint:ci":"eslint .","lint:staged":"lint-staged","test":"node --require ts-node/register --test src/*.test.ts"},"repository":{"type":"git","url":"git+https://github.com/wix-incubator/videokitten.git"},"keywords":["video","recording","adb","xcrun","scrcpy","ios","android","simulator","emulator","screen-recording"],"author":{"name":"Yaroslav Serhieiev","email":"yaroslavs@wix.com"},"license":"MIT","bugs":{"url":"https://github.com/wix-incubator/videokitten/issues"},"homepage":"https://github.com/wix-incubator/videokitten#readme","dependencies":{"execa":"^5.1.1"},"devDependencies":{"@commitlint/cli":"^17.4.2","@commitlint/config-conventional":"^17.4.2","@eslint/js":"^9.13.0","@types/chai":"^4.0.1","@types/eslint__js":"^8.42.3","@types/node":"^20.17.9","@typescript-eslint/eslint-plugin":"^8.4.0","@typescript-eslint/parser":"^8.4.0","chai":"^4.0.0","cz-conventional-changelog":"^3.3.0","esbuild":"^0.24.0","eslint":"~9.14.0","eslint-config-prettier":"^9.1.0","eslint-plugin-import":"^2.31.0","eslint-plugin-jsdoc":"^50.2.2","eslint-plugin-prefer-arrow":"^1.2.3","eslint-plugin-prettier":"^5.2.1","eslint-plugin-unicorn":"^55.0.0","husky":"^9.1.7","lint-staged":"^15.2.10","prettier":"^3.4.2","semantic-release":"^24.2.0","ts-node":"^10.9.2","typescript-eslint":"^8.10.0","typescript":"^5.6.3"},"browserslist":["node 16"],"_id":"videokitten@1.0.1","gitHead":"a30a8ae10f97e2633b9c2ce116920a72c76204c1","_nodeVersion":"22.18.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-YXgnWFg5ylBTP687k6LMHGnYPNPZpzsyHuJwv6Q+DbKrAXz/zk9pZOnlc63ROp7LUGVPZvBT867F2YRTdey89A==","shasum":"cca57053e5afa86d1bd2bcb2d0ecdca27d584182","tarball":"https://registry.npmjs.org/videokitten/-/videokitten-1.0.1.tgz","fileCount":47,"unpackedSize":208289,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHQeMdhcIpSuQ4L4eThBi6yME3CLpF8nOtVmVoiURxtuAiEAuGpdbeV1C42u1M6XVjb5Zm1BUjqw0bJhicLatD9OxfM="}]},"_npmUser":{"name":"yaroslavs","email":"yaroslavs@wix.com"},"directories":{},"maintainers":[{"name":"yaroslavs","email":"yaroslavs@wix.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/videokitten_1.0.1_1755078117866_0.1838109109460604"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-12T18:33:10.517Z","modified":"2025-08-13T09:41:58.213Z","1.0.0":"2025-08-12T18:33:10.738Z","1.0.1":"2025-08-13T09:41:58.048Z"},"bugs":{"url":"https://github.com/wix-incubator/videokitten/issues"},"author":{"name":"Yaroslav Serhieiev","email":"yaroslavs@wix.com"},"license":"MIT","homepage":"https://github.com/wix-incubator/videokitten#readme","keywords":["video","recording","adb","xcrun","scrcpy","ios","android","simulator","emulator","screen-recording"],"repository":{"type":"git","url":"git+https://github.com/wix-incubator/videokitten.git"},"description":"A cross-platform Node.js library for recording videos from iOS simulators and Android devices/emulators","maintainers":[{"name":"yaroslavs","email":"yaroslavs@wix.com"}],"readme":"# Videokitten 📱🎬\n\n**Videokitten** is a cross-platform Node.js library for recording videos from iOS simulators and Android emulators/devices. It provides a simple, unified API to automate screen recording for your integration tests, E2E tests, or any other automation needs.\n\n- 🍎 **iOS Simulator Support** - Record videos using `xcrun simctl`\n- 🤖 **Android Emulator/Device Support** - Record videos using the powerful `scrcpy` tool\n- 🎥 **Flexible API** - Start and stop recording programmatically with full control.\n- 🛠️ **Error Handling** - Built-in error classification for common issues (e.g., device not found, tools not installed).\n- ✨ **TypeScript Support** - Fully typed for a great developer experience.\n\n## Installation\n\n```bash\nnpm install videokitten\n```\n\n## Quick Start\n\nHere's a basic example of how to record a 5-second video from a booted iOS simulator:\n\n```javascript\nimport { videokitten } from 'videokitten';\n\nasync function main() {\n  const kitten = videokitten({ platform: 'ios' });\n\n  console.log('Starting iOS recording...');\n  const session = await kitten.startRecording();\n  if (!session) {\n    console.log('Recording failed to start (onError: ignore was set)');\n    return;\n  }\n\n  console.log('Recording started!');\n\n  // Let it record for 5 seconds\n  await new Promise(resolve => setTimeout(resolve, 5000));\n\n  console.log('Stopping recording...');\n  const videoPath = await session.stop();\n  if (videoPath) {\n    console.log(`Video saved to: ${videoPath}`);\n  } else {\n    console.log('Recording failed to complete');\n  }\n}\n\nmain().catch(console.error);\n```\n\n## API\n\n### `videokitten(options)`\n\nCreates a new `Videokitten` instance.\n\n#### `options`\n\nAn object with platform-specific configuration.\n\n##### iOS Options (`VideokittenOptionsIOS`)\n\n```typescript\nconst options = {\n  platform: 'ios',\n  deviceId?: string;       // Default: 'booted' (the currently booted simulator)\n  outputPath?: string;     // Default: a temporary file in /tmp\n  xcrunPath?: string;      // Default: '/usr/bin/xcrun'\n  codec?: 'h264' | 'hevc'; // Default: 'hevc'\n  display?: 'internal' | 'external'; // Default: 'internal'\n  force?: boolean;         // Overwrite existing file. Default: false\n};\n```\n\n##### Android Options (`VideokittenOptionsAndroid`)\n\nVideokitten uses `scrcpy` for Android recording, so the options are a direct mapping to `scrcpy`'s command-line arguments.\n\n```typescript\nconst options = {\n  platform: 'android',\n  deviceId?: string;          // Target a specific device by serial\n  outputPath?: string;        // Default: a temporary file in /tmp\n  scrcpyPath?: string;        // Path to scrcpy executable. Default: 'scrcpy'\n  adbPath?: string;           // Path to adb executable. Default: assumes in PATH\n\n  // See scrcpy documentation for all available options\n  recording?: {\n    bitRate?: number;         // e.g., 8_000_000 for 8 Mbps\n    codec?: 'h264' | 'h265' | 'av1';\n    format?: 'mp4' | 'mkv';\n    timeLimit?: number;       // In seconds\n  },\n\n  // And many more...\n};\n```\n\n### Base Options\n\nAll platforms support these common options:\n\n```typescript\nconst options = {\n  platform: 'ios' | 'android',\n  deviceId?: string;          // Device identifier\n  outputPath?: string;        // Output file path\n  abortSignal?: AbortSignal;  // Signal to cancel recording\n  onError?: 'throw' | 'ignore' | ((error: Error) => void);\n  timeout?: number;           // Recording timeout in seconds\n  delay?: number | [number, number]; // Frame buffering delays in milliseconds\n};\n```\n\n#### Delay Configuration\n\nThe `delay` option controls timing delays for frame buffering:\n\n- **Single number**: Startup delay only (e.g., `200` = wait 200ms after process is ready)\n- **Tuple `[startup, stop]`**: Both startup and stop delays (e.g., `[200, 100]`)\n\n**Startup delay**: Waits after the process signals it's ready before considering recording started. This allows processes like scrcpy to initialize and buffer frames.\n\n**Stop delay**: Waits before stopping the process to ensure all buffered frames are written. This prevents missing the last few frames of the recording.\n\n**Defaults**:\n- **Android**: `200` - scrcpy needs time to buffer frames\n- **iOS**: `0` - iOS handles buffering internally\n\n```typescript\n// Custom delays for Android\nconst android = videokitten({\n  platform: 'android',\n  delay: [300, 150] // 300ms startup, 150ms stop delay\n});\n\n// Just startup delay\nconst android = videokitten({\n  platform: 'android',\n  delay: 250 // 250ms startup delay only\n});\n```\n\n### `kitten.startRecording(overrideOptions)`\n\nStarts a new recording session.\n\n- `overrideOptions`: An optional object to override the options provided to the `videokitten` constructor.\n\nReturns a `Promise<RecordingSession | undefined>`. Returns `undefined` if recording fails to start and `onError` is set to `'ignore'`.\n\n### `RecordingSession`\n\nAn object representing an active recording session.\n\n#### `session.stop()`\n\nStops the recording gracefully and returns the path to the saved video file.\n\nReturns a `Promise<string | undefined>`. Returns `undefined` if recording fails to complete and `onError` is set to `'ignore'`.\n\n## Advanced Usage\n\n### Stopping with an `AbortSignal`\n\nYou can use a standard `AbortSignal` to stop the recording. This is useful for integrating with other parts of your application that use abort controllers.\n\n```javascript\nimport { videokitten } from 'videokitten';\n\nasync function recordWithSignal() {\n  const kitten = videokitten({ platform: 'android' });\n  const controller = new AbortController();\n\n  // Abort after 10 seconds\n  setTimeout(() => controller.abort(), 10000);\n\n  try {\n    const session = await kitten.startRecording({ abortSignal: controller.signal });\n    if (!session) {\n      console.log('Recording failed to start (onError: ignore was set)');\n      return;\n    }\n\n    console.log('Recording... press Ctrl+C or wait 10s to stop.');\n\n    // The `stop()` promise will be rejected with an AbortError\n    // when the signal is aborted.\n    const videoPath = await session.stop();\n    if (videoPath) {\n      console.log(`Video saved to: ${videoPath}`);\n    } else {\n      console.log('Recording failed to complete');\n    }\n  } catch (error) {\n    if (error.name === 'AbortError') {\n      console.log('Recording was aborted successfully.');\n    } else {\n      console.error('An unexpected error occurred:', error);\n    }\n  }\n}\n\nrecordWithSignal();\n```\n\n### Error Handling\n\nVideokitten throws specific error classes to help you handle different failure scenarios.\n\n```typescript\nimport { videokitten, VideokittenError, VideokittenXcrunNotFoundError } from 'videokitten';\n\ntry {\n  const kitten = videokitten({ platform: 'ios', xcrunPath: '/invalid/path' });\n  const session = await kitten.startRecording();\n  if (!session) {\n    console.log('Recording failed to start (onError: ignore was set)');\n    return;\n  }\n\n  const videoPath = await session.stop();\n  if (videoPath) {\n    console.log(`Video saved to: ${videoPath}`);\n  }\n} catch (error) {\n  if (error instanceof VideokittenXcrunNotFoundError) {\n    console.error('xcrun is not installed or not in the correct path!');\n  } else if (error instanceof VideokittenError) {\n    console.error('A videokitten error occurred:', error.message);\n  } else {\n    console.error('An unknown error occurred:', error);\n  }\n}\n```\n\nAvailable error classes:\n\n- `VideokittenError` (base class)\n- `VideokittenDeviceNotFoundError`\n- `VideokittenXcrunNotFoundError`       // xcrun tool not found (iOS)\n- `VideokittenScrcpyNotFoundError`      // scrcpy tool not found (Android)\n- `VideokittenAdbNotFoundError`         // adb tool not found (Android)\n- `VideokittenIOSSimulatorError`\n- `VideokittenAndroidDeviceError`\n- `VideokittenFileWriteError`\n- `VideokittenOperationAbortedError`\n- `VideokittenRecordingFailedError`\n\n## Requirements\n\n- **Node.js**: v16.14.0 or higher\n- **iOS**: macOS with Xcode Command Line Tools installed.\n  - `xcrun` tool (usually available at `/usr/bin/xcrun`)\n- **Android**: `scrcpy` and `adb` must be installed and available in your system's `PATH`.\n  - [scrcpy releases](https://github.com/Genymobile/scrcpy/releases)\n  - [Android SDK Platform-Tools](https://developer.android.com/studio/releases/platform-tools)\n\n## License\n\nMIT\n\n## Contributing\n\nContributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).\n","readmeFilename":"README.md"}