@royalti/syynk 0.1.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 +279 -0
- package/dist/index.d.mts +571 -0
- package/dist/index.d.ts +571 -0
- package/dist/index.js +587 -0
- package/dist/index.mjs +548 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# @royalti/syynk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the Syynk API - transcribe audio and export synchronized lyrics/subtitles.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @royalti/syynk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @royalti/syynk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @royalti/syynk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { SyynkClient } from '@royalti/syynk';
|
|
19
|
+
|
|
20
|
+
const client = new SyynkClient({
|
|
21
|
+
apiKey: process.env.SYYNK_API_KEY,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Transcribe audio from URL
|
|
25
|
+
const result = await client.transcribe({
|
|
26
|
+
audioUrl: 'https://example.com/song.mp3',
|
|
27
|
+
language: 'en',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Export to LRC format
|
|
31
|
+
const lrc = await client.export({
|
|
32
|
+
format: 'lrc',
|
|
33
|
+
segments: result.segments,
|
|
34
|
+
projectName: 'My Song',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log(lrc.content);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- Transcribe audio from URL or file upload
|
|
43
|
+
- Export to multiple formats: LRC, SRT, VTT, TTML, JSON, TXT
|
|
44
|
+
- Full TypeScript support
|
|
45
|
+
- Automatic retry with exponential backoff
|
|
46
|
+
- Rate limit handling
|
|
47
|
+
- Works in Node.js (18+) and browsers
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### `SyynkClient`
|
|
52
|
+
|
|
53
|
+
Create a new client instance:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
const client = new SyynkClient({
|
|
57
|
+
apiKey: 'your-api-key',
|
|
58
|
+
baseUrl: 'https://syynk.to', // optional
|
|
59
|
+
timeout: 30000, // optional, in milliseconds
|
|
60
|
+
maxRetries: 3, // optional
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Methods
|
|
65
|
+
|
|
66
|
+
#### `transcribe(options)`
|
|
67
|
+
|
|
68
|
+
Transcribe audio from a URL.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const result = await client.transcribe({
|
|
72
|
+
audioUrl: 'https://example.com/song.mp3',
|
|
73
|
+
language: 'en', // optional, auto-detected if not specified
|
|
74
|
+
projectName: 'My Song', // optional
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Result contains:
|
|
78
|
+
// - projectId: string
|
|
79
|
+
// - language: string
|
|
80
|
+
// - duration: number (seconds)
|
|
81
|
+
// - segments: Segment[]
|
|
82
|
+
// - words: Word[]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### `transcribeFile(file, options?)`
|
|
86
|
+
|
|
87
|
+
Transcribe audio from a file upload.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Node.js
|
|
91
|
+
import fs from 'fs';
|
|
92
|
+
|
|
93
|
+
const buffer = fs.readFileSync('song.mp3');
|
|
94
|
+
const result = await client.transcribeFile(buffer, {
|
|
95
|
+
filename: 'song.mp3',
|
|
96
|
+
language: 'en',
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Browser
|
|
100
|
+
const file = document.querySelector('input[type="file"]').files[0];
|
|
101
|
+
const result = await client.transcribeFile(file);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### `export(options)`
|
|
105
|
+
|
|
106
|
+
Export segments to a specific format.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const result = await client.export({
|
|
110
|
+
format: 'lrc', // 'lrc' | 'srt' | 'vtt' | 'ttml' | 'json' | 'txt'
|
|
111
|
+
segments: transcription.segments,
|
|
112
|
+
words: transcription.words, // optional, for word-level timing
|
|
113
|
+
projectName: 'My Song', // optional
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Result contains:
|
|
117
|
+
// - content: string
|
|
118
|
+
// - filename: string
|
|
119
|
+
// - mimeType: string
|
|
120
|
+
// - extension: string
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### `getFormats()`
|
|
124
|
+
|
|
125
|
+
Get information about available export formats.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const formats = await client.getFormats();
|
|
129
|
+
|
|
130
|
+
// Returns:
|
|
131
|
+
// [
|
|
132
|
+
// { id: 'lrc', name: 'LRC', supportsWordTiming: false, ... },
|
|
133
|
+
// { id: 'ttml', name: 'TTML', supportsWordTiming: true, ... },
|
|
134
|
+
// ...
|
|
135
|
+
// ]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### `listProjects(options?)`
|
|
139
|
+
|
|
140
|
+
List your projects with optional filtering.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const result = await client.listProjects({
|
|
144
|
+
status: 'ready',
|
|
145
|
+
type: 'lyricsync',
|
|
146
|
+
limit: 20,
|
|
147
|
+
offset: 0,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Result contains:
|
|
151
|
+
// - projects: Project[]
|
|
152
|
+
// - total: number
|
|
153
|
+
// - hasMore: boolean
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### `getProject(id, options?)`
|
|
157
|
+
|
|
158
|
+
Get a single project with segments and words.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const project = await client.getProject('project-id', {
|
|
162
|
+
includeWords: true,
|
|
163
|
+
includeSegments: true,
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### `deleteProject(id)`
|
|
168
|
+
|
|
169
|
+
Delete a project.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
await client.deleteProject('project-id');
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### `getRateLimitInfo()`
|
|
176
|
+
|
|
177
|
+
Get the current rate limit status.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const rateLimit = client.getRateLimitInfo();
|
|
181
|
+
|
|
182
|
+
if (rateLimit) {
|
|
183
|
+
console.log(`${rateLimit.remaining}/${rateLimit.limit} requests remaining`);
|
|
184
|
+
console.log(`Resets at ${new Date(rateLimit.reset * 1000)}`);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Error Handling
|
|
189
|
+
|
|
190
|
+
The SDK provides typed errors for different failure scenarios:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import {
|
|
194
|
+
SyynkClient,
|
|
195
|
+
AuthenticationError,
|
|
196
|
+
RateLimitError,
|
|
197
|
+
ValidationError,
|
|
198
|
+
NotFoundError,
|
|
199
|
+
ServerError,
|
|
200
|
+
NetworkError,
|
|
201
|
+
TimeoutError,
|
|
202
|
+
} from '@royalti/syynk';
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const result = await client.transcribe({ audioUrl: 'invalid' });
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (error instanceof AuthenticationError) {
|
|
208
|
+
// Invalid or missing API key (401)
|
|
209
|
+
console.log('Check your API key');
|
|
210
|
+
} else if (error instanceof RateLimitError) {
|
|
211
|
+
// Rate limit exceeded (429)
|
|
212
|
+
console.log(`Retry after ${error.retryAfter} seconds`);
|
|
213
|
+
console.log(`Reset time: ${error.getResetDate()}`);
|
|
214
|
+
} else if (error instanceof ValidationError) {
|
|
215
|
+
// Invalid request data (400)
|
|
216
|
+
console.log(error.fieldErrors);
|
|
217
|
+
} else if (error instanceof NotFoundError) {
|
|
218
|
+
// Resource not found (404)
|
|
219
|
+
console.log('Project not found');
|
|
220
|
+
} else if (error instanceof ServerError) {
|
|
221
|
+
// Server error (500+)
|
|
222
|
+
console.log(`Request ID: ${error.requestId}`);
|
|
223
|
+
} else if (error instanceof NetworkError) {
|
|
224
|
+
// Connection failed
|
|
225
|
+
console.log('Check your internet connection');
|
|
226
|
+
} else if (error instanceof TimeoutError) {
|
|
227
|
+
// Request timed out
|
|
228
|
+
console.log(`Timed out after ${error.timeoutMs}ms`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Type Guards
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import { isSyynkError, isRateLimitError, isRetryableError } from '@royalti/syynk';
|
|
237
|
+
|
|
238
|
+
if (isSyynkError(error)) {
|
|
239
|
+
console.log(error.code, error.status);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (isRateLimitError(error)) {
|
|
243
|
+
console.log(error.retryAfter);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (isRetryableError(error)) {
|
|
247
|
+
// Error is rate limit, network, timeout, or server error
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Types
|
|
252
|
+
|
|
253
|
+
All types are exported for TypeScript users:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import type {
|
|
257
|
+
Segment,
|
|
258
|
+
Word,
|
|
259
|
+
Project,
|
|
260
|
+
ExportFormat,
|
|
261
|
+
TranscribeResult,
|
|
262
|
+
ExportResult,
|
|
263
|
+
} from '@royalti/syynk';
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Export Formats
|
|
267
|
+
|
|
268
|
+
| Format | Extension | Word Timing | Use Case |
|
|
269
|
+
|--------|-----------|-------------|----------|
|
|
270
|
+
| LRC | `.lrc` | No | Karaoke players |
|
|
271
|
+
| SRT | `.srt` | No | Video subtitles (VLC, YouTube) |
|
|
272
|
+
| VTT | `.vtt` | No | HTML5 `<track>` element |
|
|
273
|
+
| TTML | `.ttml` | Yes | Professional broadcast |
|
|
274
|
+
| JSON | `.json` | Yes | Developer integration |
|
|
275
|
+
| TXT | `.txt` | No | Plain text lyrics |
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT
|